Why did Fortran not overload ==
or .eq.
as a binary operator between logical items instead of .eqv.
? And for that matter why not /=
instead of .neqv.
?
Not answering the why question, but if using ==
and /=
between booleans is desired, one can use the M_overload module of @urbanjost.
In case the full version with things like allowing strings that represent numbers in int, dble, … is
too much, the abridged version:
module logical_ops
implicit none
interface operator(==)
module procedure boolean_equal
end interface
interface operator(/=)
module procedure boolean_notequal
end interface
contains
logical function boolean_equal(L1, L2)
logical, intent(in) :: L1, L2
boolean_equal = L1 .eqv. L2
end function boolean_equal
logical function boolean_notequal(L1, L2)
logical, intent(in) :: L1, L2
boolean_notequal = L1 .neqv. L2
end function boolean_notequal
end module logical_ops
program testit
! ifort/ifx allow this as an extension, so use "ifx -stand=f90" to see the effect
use logical_ops
write (*, *) merge( '== passes', '== fails ', (.true. == .true.) )
write (*, *) merge( '=/ passes', '=/ fails ', (.true. /= .false.) )
end program testit
Just curious, why use merge() here? Why not the simpler L1 .eqv. L2
?
No reason other than started from another piece of code and did not think to remove it. Good point. Will change it.
That looks much better! Thanks.
The fact that L1 == L2
is somehow ill-defined in Fortran is utterly deranged. Why can’t this be fixed?
Not deranged at all. The internal value of a logical is not defined by the standard. Two logicals could have different internal values and still be both .TRUE. or .FALSE… There’s a reason .EQV. exists.
But who would write a compiler like that? Does such a compiler currently exist? Why not standardize what most (basically all) users would expect? If they are both true then == equates to true, etc.
This is about not breaking old, badly written code, isn’t it?
Re: “Why can’t this be fixed?” well, there is no logical explanation for it!!!
Re: the blog linked upthread, is the following blurb logical?
The way many Fortran programmers would naturally do this is as follows:
IF (LOGVAL1 .EQ. LOGVAL2) …
but the results of this can vary depending on the internal representation.
It will be interesting to come across the processor that has different internal representation for LOGVAL1
and LOGVAL2
!
This is the thing that has always bothered me, even before f77. Life would be so much simpler if the standard had long ago defined .false. to be 0 and .true. to be 1, and then allowed those values to be used as integers in arithmetic expressions and as array indexes.
As for compilers having many-to-one mappings from internal values to the two logical values, that is what the C language has always done and will probably always do. As discussed in the Doctor Fortran article above, in C an integer zero is false and any other integer value is true. This was all standard practice before bool was added to the language decades later.
Oddly, the convention with return codes in shells is the opposite. A return code of 0 means success, and a nonzero return code is a failure. In shell scripts, expressions like prog1 && prog2 && prog3
do what you expect them to do, but the underlying return codes are the opposite of the C convention.
If x
and y
are logical variables, a Fortran compiler could say x == y
is .false.
if x
and y
have different KINDs even if they are both true or both false. (Since x == y
is nonstandard, the compiler could do anything.) If the natural interpretation of x == y is standardized, some compilers in standard-conforming mode could be forced to change what a code does, and the committee may wish to avoid this.
Since the language allows for overloading the == and /= operator to act on LOGICAL values as intuitively expected, it seems hard to argue it cannot be fixed; and it is a common extension to allow it. It does seem what is currently standardized should be relegated to requiring a compiler switch; especially since the internal bit representation is not defined by the standard. I think that means any old code exploiting the current state would by definition (well, I guess by lack of definition) be non-portable.
Is there an example of what would break if the behavior defined by the example overload were standard?
I think it was CDC and early COS Cray machines that had a “real” logical that only took one bit that saved a lot of memory back in the day. The other puzzling thing to me is why the standard specifies a default REAL, INTEGER, and LOGICAL take the same amount of storage. I assume that is to save some old code from breaking that EQUIVALENCEd some variable of different type, which I think was always considered non-standard and problematic.
I very often find that defining all the logicals to be the smallest kind supported saves significant space when large LOGICAL arrays are used, and often performs better as well. I can imagine that when only a few LOGICAL values are used that some hardware would align and optimize best with variables of a “standard” size but that would be a very unusual reason for the standard to specify the size like that. So another mystery to me is why the standard specified the size instead of leaving it up to the compiler implementations.
The @sblionel article is the best description I remember seeing of why non-standard use of LOGICAL values is not portable; but I do not see a reason the standard cannot specify more intuitive behavior except to save some non-portable old behavior (which would better be supported as an extension instead of being protected by the standard IMO).
Any explanation of good reasons it was codified would be enlightening if anyone can think of them.
One of my most common reasons for overloading == and =/ with something like the example is to use a code that uses the common extension that those work with logicals. I think the main reason so much code does not use .EQV. and .NEQV. properly is that so many compilers have the extension, which seems like a bad state of affairs.
Thank you sblionel. But urbanjost has shown us a module overloading ==
and /=
between logical items by making use of .EQV.
and .NEQV.
Could that ploy be defeated by a compiler that allows more than one internal representation of .true.
or of .false.
? Of course, even if the result would always be the same, ==
and /=
would have their usual precedence, and not the precedence of .EQV.
and .NEQV.
that sblionel warned us about. Beliavsky’s objection seems to be that if a
and b
are both logical but of different kinds and a compiler has an extension allowing a==b
then
(a==b) .EQV. (a.EQV.b)
might be false. Do any compilers do that?
Two more possible (harmless?) overloads are:
-
if
a
andb
are of type character ,a+b
can be given the same result asa//b
except that it would have the precedence of+
instead of//
-
if
n
is a nonnegative integer andc
is of type character,n*c
andc*n
can be given the same result asrepeat(n,c)
. DATA statements already allown*c
ifn
andc
are both constant.
The following FPP snippet appears over 120 times in individual files in our codebase because ==
is undefined for logical
type in the Fortran standard.
#if IS_LOGICAL
#define IS_EQUAL .eqv.
#else
#define IS_EQUAL ==
#endif
It’s unfair to expect the standard committee to make backward-incompatible changes to the language. But it is also equally or more unfair to expect modern Fortran programmers to endure so much pain with the lack of such simple generic tools and operations. There should be a middle ground to resolve such minor but annoying incompatibilities.
The standard might require the compilers to give the expected result of (A == B)
(i.e. .true.
iif both A
and B
are .true.
), exactly the same way it requires it for the .EQV.
operator, regardless the internal representations.
Similarly, bit manipulation functions on integers assume a representation model of the integers, regardless the actual internal representation of the integers in memory, which could be entirely different from the model (I’m not even sure that the standard requires that a given integer value has a unique internal representation?).
I tend to prefer the explanations given in the gfortran documentation, about the possible ambiguities if ==
was used :
Is there a detailed proposal anywhere for adding LOGICAL == LOGICAL
to the language? In particular, what would the precedence of ==
be (see the gfortran link posted by @PierU), and what happens if there is already a user-defined operator?
This seems like it would be nice to have, but I worry that the answer to those two questions would just add different warts to the language. At least the current one is easily identified: you use ==
and the compiler asks if you meant .eqv.
.
I don’t understand this. Do you then write x IS_EQUAL y
instead of x .eqv. y
? Why?
My opinion is that the precedence issue detailed on the gfortran page is a good enough reason to not have ==
and /=
as operators for logical types.
If I followed the previous discussions correctly, the issue is not the result, the issue is that the .eqv.
and ==
operators have different precedence. If that precedence is changed, then it could break previous code, including both code that uses language extensions for specific compilers and also standard conforming code. If the programmer was cautious and used redundant parentheses to enforce precedence within expressions, then the code would survive such a change. Otherwise, it might introduce a very difficult to find bug.
Could someone who understands this precedence issue well post an example of an expression that evaluates differently if the operator precedence were changed?