Node: Loops, Next: , Previous: Block Data and Libraries, Up: Collected Fortran Wisdom



Loops

The meaning of a DO loop in Fortran is precisely specified in the Fortran standard...and is quite different from what many programmers might expect.

In particular, Fortran iterative DO loops are implemented as if the number of trips through the loop is calculated before the loop is entered.

The number of trips for a loop is calculated from the start, end, and increment values specified in a statement such as:

     DO iter = start, end, increment
     

The trip count is evaluated using a fairly simple formula based on the three values following the = in the statement, and it is that trip count that is effectively decremented during each iteration of the loop. If, at the beginning of an iteration of the loop, the trip count is zero or negative, the loop terminates. The per-loop-iteration modifications to iter are not related to determining whether to terminate the loop.

There are two important things to remember about the trip count:

These two items mean that there are loops that cannot be written in straightforward fashion using the Fortran DO.

For example, on a system with the canonical 32-bit two's-complement implementation of INTEGER(KIND=1), the following loop will not work:

     DO I = -2000000000, 2000000000
     

Although the start and end values are well within the range of INTEGER(KIND=1), the trip count is not. The expected trip count is 40000000001, which is outside the range of INTEGER(KIND=1) on many systems.

Instead, the above loop should be constructed this way:

     I = -2000000000
     DO
       IF (I .GT. 2000000000) EXIT
       ...
       I = I + 1
     END DO
     

The simple DO construct and the EXIT statement (used to leave the innermost loop) are F90 features that g77 supports.

Some Fortran compilers have buggy implementations of DO, in that they don't follow the standard. They implement DO as a straightforward translation to what, in C, would be a for statement. Instead of creating a temporary variable to hold the trip count as calculated at run time, these compilers use the iteration variable iter to control whether the loop continues at each iteration.

The bug in such an implementation shows up when the trip count is within the range of the type of iter, but the magnitude of ABS(end) + ABS(incr) exceeds that range. For example:

     DO I = 2147483600, 2147483647
     

A loop started by the above statement will work as implemented by g77, but the use, by some compilers, of a more C-like implementation akin to

     for (i = 2147483600; i <= 2147483647; ++i)
     

produces a loop that does not terminate, because i can never be greater than 2147483647, since incrementing it beyond that value overflows i, setting it to -2147483648. This is a large, negative number that still is less than 2147483647.

Another example of unexpected behavior of DO involves using a nonintegral iteration variable iter, that is, a REAL variable. Consider the following program:

           DATA BEGIN, END, STEP /.1, .31, .007/
           DO 10 R = BEGIN, END, STEP
              IF (R .GT. END) PRINT *, R, ' .GT. ', END, '!!'
              PRINT *,R
     10    CONTINUE
           PRINT *,'LAST = ',R
           IF (R .LE. END) PRINT *, R, ' .LE. ', END, '!!'
           END
     

A C-like view of DO would hold that the two "exclamatory" PRINT statements are never executed. However, this is the output of running the above program as compiled by g77 on a GNU/Linux ix86 system:

      .100000001
      .107000001
      .114
      .120999999
      ...
      .289000005
      .296000004
      .303000003
     LAST =   .310000002
      .310000002 .LE.   .310000002!!
     

Note that one of the two checks in the program turned up an apparent violation of the programmer's expectation--yet, the loop is correctly implemented by g77, in that it has 30 iterations. This trip count of 30 is correct when evaluated using the floating-point representations for the begin, end, and incr values (.1, .31, .007) on GNU/Linux ix86 are used. On other systems, an apparently more accurate trip count of 31 might result, but, nevertheless, g77 is faithfully following the Fortran standard, and the result is not what the author of the sample program above apparently expected. (Such other systems might, for different values in the DATA statement, violate the other programmer's expectation, for example.)

Due to this combination of imprecise representation of floating-point values and the often-misunderstood interpretation of DO by standard-conforming compilers such as g77, use of DO loops with REAL iteration variables is not recommended. Such use can be caught by specifying -Wsurprising. See Warning Options, for more information on this option.