Pretty sure LOGICAL variables and operators were introduced with Fortran IV. At one point (and probably still the case) they operated with the same operators as Integers and could be compared and cast in expressions as an integer; but in a non-portable way as nothing defined the internal representation other than there was a value used for .true. and .false.
Since everyone had/has such an extension cleaning it up would have meant someone having to change. Cleaning it up would have either meant:
-
not allowing logical values to be treated as integers, so it would be an error to mix logical and numeric values; like character variables. That would have broken the already well-established usage.
-
The other direction would have meant specifying exactly what the internal representation was, which would have also broken existing code.
So instead, new operators were created and it was declared the existing mixing was non-standard and not recognized by the standard at all so compilers could continue to do what they were doing (which was non-portable) but that the user had a new portable syntax.
That’s the way I remember it. A bad compromise in some respects, but it provided a solution that provided for creating portable code.
So there is no error from things like “.true.+1”, “.true.<.false.” and so on on most if not all early compilers.
So if no one was going to bite the bullet and address it in one of the obvious ways, another option might have been something like “implicit none” were the compiler only had to follow specific rules like .true is 0 and .false is 1 and no other value (pick your two values, but they had to be picked) or that you could specifiy something like “implicit true (0), false(1)”. That would have seemed reasonable to me, but thinking of how many comments about the horrors of “implicit ANYTHING” I have seen I am not that would have worked out as simply as I thought.
Other than at least recent versions of gfortran, who does not allow a<b == 0 or a<b == .true. and so on? Three out of four that I tried compile and run the following program They do not necessarily get the same answers but it runs
program testit
integer :: a=10, b=20
write(*,*) a>b +3, a<b /10 ,( a>b) == (b<a), 1==.true., 1==.false., (a>b) == (b<a)
write(*,*) .true.+0, .false.+0
end program testit
although one would not compile “a>b == b<a” and required parenthesis and another did not.
When it comes up for me it is usually converting something that uses the extensions to run on a different compiler. If there are a few of them it is easy to change; if there are a lot I can use a module to overload it as required. Theoretically that could be a lot of code but I have not personally run into a case where all the logicals were not the same kind, and that a single value was assumed true (ie nothing like “if (4.eq…true.)” so it has just taken a few lines. At least a few compilers have switches to insist on standard-conforming usage which makes it easy to locate the non-standard usage.
That is how I remember it; the past behavior I miss is one one system logical was an actual bit, which let you have very large arrays of logical values back when memory was in really short supply; I do not thing anyone does that.
And I almost always use 1-byte logicals instead of the default if I have arrays of any significant size and/or performance is an issue. It did not use to be the case, but lately i think 1-byte kinds are always faster or at least the same speed of default kind logicals.
r
How I remember it probably has to do with what compilers I used and might be different for others, since it is all about undefined behavior and compilers varied tremendously in the past.
Oddly I remember IBM as having almost no extensions; whereas I remember VAX/VMS Fortran as being quite capable of writing system-level programs, but in a totally non-portable way. Fortran started as IBM so maybe it was not as restrictive as I remember.