Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Logical expressions in Fortran 90

benh
Beginner
3,089 Views
We have a lot of legacy code that I was tempted to try to compile with IVF 8.1 with "strict Fortran 90" option (on Win32). File format is "fixed 132 columns". It now almost compiles successfully, but especially a couple of issues with logical variables seem a bit strange to me:


1. Consider the following fragment of code:

logical(2) subm(100)
common /srfsl3/ subm !+ some more vars...
::: ! This compiles OK:
if ( subm(i) .or. subm(j) ) return
::: ! Compile error in next statement:
if (subm(isp) .ne. subm(i)) call msg_warning

The error message is:

"Error: Fortran 90 does not allow a LOGICAL data type in this context. [subm]"


2. Furthermore, code fragments similar to this is often found:

logical first
data first /.true./

if (first .eq. .true.) then ! error
first = .false.
! ...some processing...
endif

Error message is:

"Error: Fortran 90 does not allow a LOGICAL data type in this context. [first]"



Issue 1 (in its simplest form) I can get around by replacing ".ne."/".eq." with ".neqv."/".eqv.". Similarly issue 2 can also be "fixed" by replacing with "if (first) then ...". However, in many places this kind of explicitly comparing the values of the logical variables were introduced because that was the only thing that produced correct code with [a former version of] the CVF compiler, so there are concerns about letting this still be written that way!

(My priority is to make the code as portable as possible among different compilers, while at the same time not breaking any annoying quirks with specific known compilers that we use. I believe trying to adhere strictly to a common standard will help achieving that goal...)


To conclude, it seems that many operators (including .eq.) do not allow the "logical" data type as its arguments, not even the constants .false. and .true., although they seem perfectly logical (no pun intended) to use in my view. Is this really the intended behavior in the Fortran 90 standard, or is this an issue with IVF?

If it is intended behavior, does anybody know the rationale behind such a decision in the standard?


-+-Ben-+-
0 Kudos
7 Replies
Steven_L_Intel1
Employee
3,088 Views
Ordinarily, I would point you at my "Doctor Fortran" article on this topic, but HP has taken down a lot of the old CVF site, so I'll reproduce the text here. (Actually, what I should do is repost all of the articles in this forum - I'll get to that someday):

Doctor Fortran in "To .EQV. or to .NEQV., that is the question", or "It's only LOGICAL"

By Steve Lionel
Visual Fortran Engineering

Most Fortran programmers are familiar with the LOGICAL data type, or at least they think they are.... An object of type LOGICAL has one of only two values, true or false. The language also defines two LOGICAL constant literals .TRUE. and .FALSE., which have the values true and false, respectively. It seems so simple, doesn't it? Yes... and no.

The trouble begins when you start wondering about just what the binary representation of a LOGICAL value is. An object of type "default LOGICAL kind" has the same size as a "default INTEGER kind", which in Visual Fortran (and most current Fortran implementations) is 32 bits. Since true/false could be encoded in just one bit, what do the other 31 do? Which bit pattern(s) represent true, and which represent false? And what bit patterns do .TRUE. and .FALSE. have? On all of these questions, the Fortran standard is silent. Indeed, according to the standard, you shouldn't be able to tell! How is this?

According to the standard, LOGICAL is its own distinct data type unrelated to and not interchangeable with INTEGER. There is a restricted set of operators available for the LOGICAL type which are not defined for any other type: .AND., .OR., .NOT., .EQV. and .NEQV.. Furthermore, there is no implicit conversion defined between LOGICAL and any other type.

"But wait," you cry! "I use .AND. and .OR. on integers all the time!" And so you do - but doing so is non-standard, though it's an almost universal extension in today's compilers, generally implemented as a "bitwise" operation on each bit of the value, and generally harmless. What you really should be using instead is the intrinsics designed for this purpose: IAND, IOR and IEOR.

Not so harmless is another common extension of allowing implicit conversion between LOGICAL and numeric types. This is where you can start getting into trouble due to implementation dependencies on the binary representation of LOGICAL values. For example, if you have:

INTEGER I,J,K
I = J

just what is the value of I? The answer is "it depends", and the result may even vary within a single implementation. Compaq Fortran traditionally (since the 1970s, at least) considers LOGICAL values with the least significant bit (LSB) one to be true, and values with the LSB zero to be false. All the other bits are ignored when testing for true/false. Many other Fortran compilers adopt the C definition of zero being false and non-zero being true. (Visual Fortran offers the /fpscomp:logicals switch to select the C method, since PowerStation used it as well.) Either way, the result of the expression J
The real trouble with making assumptions about the internal value of LOGICALs is when you try testing them for "equality" against another logical exp ression. 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. The Fortran language defines two operators exclusively for use on logical values, .EQV. ("equivalent to") and .NEQV. ("not equivalent to"). So the above test would be properly written as:

IF (LOGVAL1 .EQV. LOGVAL2) ...

In the Doctor's experience, not too many Fortran programmers use .EQV. and .NEQV. where they should, and get into trouble when porting software to other environments. Get in the habit of using the correct operators on LOGICAL values, and you'll avoid being snared by implementation differences.

However, there is one aspect of these operators you need to be aware of... A customer recently sent us a program that contained the following statement:

DO WHILE (K .LE. 2 .AND. FOUND .EQV. .FALSE.)

The complaint was that the compiler "generated bad code." What the programmer didn't realize was that the operators .EQV. and .NEQV. have lower precedence than any of the other predefined logical operators. This meant that the statement was treated as if it had been:

DO WHILE (((K .LE. 2) .AND. FOUND) .EQV. .FALSE.)

what was wanted instead was:

DO WHILE ((K .LE. 2) .AND. (.FOUND .EQV. .FALSE.))

The Doctor's prescription here is to always use parentheses! That way you'll be sure that the compiler interprets the expression the way you meant it to! (And you therefore don't have to learn the operator precedence table you can find in chapter 4 of the Compaq Fortran Language Reference Manual!)

Message Edited by sblionel on 04-14-2005 10:01 AM

0 Kudos
durisinm
Novice
3,088 Views
Steve,
Please do repost all the past Visual Fortran newsletters. I found that they were very informative.
I know that your plates are full, but I'm waiting to see a new issue appear.
Mike D.
0 Kudos
Steven_L_Intel1
Employee
3,088 Views
I will probably resume my Doctor Fortran column, at least. I have several topics in mind.
0 Kudos
benh
Beginner
3,088 Views
Thanks for your informative posting, Steve!
I will also be looking forward to a collection of articles like that readily accessible.

Surely it doesn't impose a major problem to write ".eqv." instead of ".eq." for logical variables, except perhaps for compatibility with legacy Fortran 77 code (not uncommon I guess).

Regarding the actual representation of .TRUE. and .FALSE., I'm pretty sure the mentioned problems that we experienced with CVF weren't resulting from conversion from other types into logical. Thus it is still a bit unexpected to have observed that behavior due to incorrect assumptions about bit patterns, because effectively what we did was then something like

logvar = .TRUE.
if (logvar .eq. .TRUE.) then ...

However, if we assume that the above assigment (or similar initialization) didn't really copy all the bits of the .TRUE. internal value, but say just set the LSB bit of logvar to 1 and left the other bits uninitialized/random instead, then that could explain why the .eq. test failed.

BTW, sometimes adding an extra set of parentheses around the IF expression also changed the behavior:

if ((logvar .eq. .TRUE.)) then ...

Would this force an extra set of type conversion to be introduced, perhaps? (Effects were often different in non-optimized vs optimized code.)


-+-Ben-+-
0 Kudos
Steven_L_Intel1
Employee
3,088 Views
If you assigned the literal value .TRUE. and then tested it with .EQ., that would work. It is non-standard, but it would work. But you can't depend on logical expressions to be equal (.EQ.) to .TRUE. or .FALSE.. They may be or may not be.

The extra parentheses do nothing in this context.

As I noted in the article, incorrect usage of logical types goes hidden until you move to a different compiler (or different compiler version sometimes.) It's good that you looked at the standards warnings, as they can highlight such problems. I have been, for a while now, agitating for the compiler to offer optional "usage" warnings for code that, while accepted by the compiler, is not likely to be what you want. Conversion of logical to numeric is one of these.
0 Kudos
benh
Beginner
3,088 Views
I see. So if I understand this correctly, given this code fragment:

logical L1, L2
integer I1, I2
I1 = 1
I2 = 0
L1 = .TRUE.
L2 = (I1.gt.0) !or a more complicated expr

Then I can, assuming I expect all IF tests to be interpreted as logically true, trust these:

if (L1.eq..true.) ...
if (L1.eqv.L2) ...
if (L2.eqv..true.) ...
if (L2.eqv.(I2.eq.0)) ...

But I cannot trust these:

if (L1.eq.L2) ...
if (L2.eq..true.) ...
if (L2.eq.(I2.eq.0)) ...

I.e., even though L2 effectively is TRUE, we cannot depend on this the same way that we can a variable that has been assigned the literal value .TRUE. Hopefully I got this correctly now... :-)
0 Kudos
Steven_L_Intel1
Employee
3,088 Views
All correct.
0 Kudos
Reply