In past issues of the newsletter, Dr. Fortran has discussed an assortment of things, sometimes obscure, that the Fortran standard says. In this issue, he’s going to take a page from Sherlock Holmes and talk about things that the standard doesn’t say, and how they can bite you as well.
Let’s start with a simple observation that the standard describes a “standard-conforming program”. That is, it establishes the rules to which a program must conform in order to produce results as specified by the standard. If your program is not standard-conforming, then all bets are off – the processor (compiler and run-time environment) can do anything (a common example used in comp.lang.fortran is “Start World War III”, though the Doctor is not aware of any implementations which would do this – he would consider this a “quality of implementation issue”).
You’ve probably written many non-conforming programs without realizing it. Got
INTEGER*4 in your programs? Non-standard. Use LOGICAL variables in arithmetic expressions (or use logical operators such as
.AND. on integers)? Non-standard. What these do is implementation-dependent. If a compiler supports these and similar uses, it does so as extensions to the standard and is generally required to have the ability to detect the non-conformance at compile-time. If your program uses such extensions, it is non-portable and may execute differently on different platforms or with different compilers.
However, there is another class of non-conformity that, in general, can’t be detected at compile time and which can cause big headaches for programmers who make unwarranted assumptions. Let’s start with one of the Doctor’s favorites – order of evaluation of LOGICAL expressions.
Many programmers write something like this:
IF ((I .NE. 0) .AND. (ARRAY(I) .NE. -1) THEN
and expect that if
I is zero, then the reference to
ARRAY(I) won’t happen. The program may work on one platform, but get array bounds errors when ported to another. However, the standard allows the operands of a logical operator to be evaluated in any order, and at any level of completeness, as long as the result is algebraically correct. For logical expressions, Fortran does NOT have strict left-to-right ordering nor does it have short-circuit evaluation. The standard-conforming way of writing this is:
IF (I .NE. 0) THEN IF (ARRAY(I) .NE. -1) THEN
Here’s another place where the standard’s silence can trap the unwary. What do you see when you execute the following statement?
WRITE (*,'(F3.0)') 2.5
Many Fortran programmers expect “3.”. But try this in Visual Fortran, as well as in most other PC and UNIX workstation Fortran implementations and you’ll get “2.”! Why? Well, the Fortran standard says that the value is to be “rounded” but doesn’t define what that means! On systems which implement IEEE floating arithmetic, the IEEE default rounding rules are used and they specify that if the rounding digit is exactly half-way between two representable results, you round so that the low-order digit is even. If you’re a VAX user, you’ll get “3.” because VAX rounding uses the “5-9 rounds up” rule, and an OpenVMS Alpha user can see it either way, depending on whether or not IEEE float was selected! The Doctor notes that the Fortran standards committee is working on a proposal for a future standard [See intrinsic module IEEE_ARITHMETIC in Fortran 2003 – Steve] that would allow the programmer to specify the rounding method, but for now, the standard is silent and you get whatever the compiler writers think is right.
Pop quiz time – in a
CHARACTER(LEN=n) declaration, what is the lowest value of n that a compiler is required to support, according to the standard? Is it A) 1? B) 11? C) 255? D) 1000? The standard doesn’t explicitly say, but one can make a good argument for one of these. Go to the end of the column to see which one and why. The Doctor’s point is that there are many compiler limits which the standard does not specify (including things such as the number of nested parentheses in an expression, number of actual arguments supported, etc.). While most implementations have reasonable limits for such things, the Doctor has seen programs which exceed the limits of some implementations (for example, using hundreds of actual arguments) and become non-portable. Just because one compiler supports something, that doesn’t mean that all will!
There are many other things the standard doesn’t say that programmers often take for granted. For example, the standard doesn’t even say that 1+1=2, or how accurate the SIN intrinsic must be. An implementation which grossly violates reasonable expectations here would probably be a commercial failure, but it wouldn’t be violating the standard!
In summary, writing standard-conforming and portable programs is not just a matter of throwing the “standards checking switch”. You also need to be aware of things the standard doesn’t say and to make sure that your application doesn’t depend on implementation-dependent features and behaviors. The more platforms you port your application to, the more likely it is that you’ll uncover such assumptions in your code.
Answer to Dr. Fortran’s pop quiz: B) 11. Why? Because INQUIRE(FORM=) is supposed to assign the value “UNFORMATTED” to the specified variable (for unformatted connections) and that’s 11 characters long, the longest of the set that INQUIRE returns. No other language rule implies a longer minimum length.
(From Intel Developer Zone, copied with permission)