ZetCode

Fortran loops

last modified November 14, 2026

Fortran has one looping construct, the do construct, with several forms. The counted form runs a fixed number of times and is the one used most; do while repeats as long as a condition holds; a bare do with an exit inside runs until something stops it. The statements exit and cycle leave a loop or skip to its next iteration, and a loop may be named so that an inner loop can leave an outer one. This article shows each form and finishes with a small prime number program.

The counted do loop

The counted loop has the form do variable = first, last and runs once for every integer from first to last inclusive. The default step is 1. The construct ends with end do, and the loop body must not change the loop variable. The counter is an ordinary integer variable, so it still exists after the loop.

do1.f90
program do1
    implicit none
    integer :: i

    do i = 1, 5
        print *, i, i * i
    end do
end program do1

The body runs five times, with i taking the values 1 to 5, and prints each number next to its square. The loop bounds may be any integer expressions, including variables and function calls, and they are evaluated once, before the first iteration. This is different from a while loop in C, where the condition is re-evaluated every time.

$ gfortran do1.f90 -o do1
$ ./do1
           1           1
           2           4
           3           9
           4          16
           5          25

Step size and descending loops

A third value after the bounds is the step, which may be negative. With a step of 2 the loop visits every second number, and with a negative step it counts down. The loop stops when the counter passes the end value in the direction of the step, so a step that does not divide the range evenly simply stops at the last value that fits.

dostep.f90
program dostep
    implicit none
    integer :: i

    do i = 0, 10, 2
        write (*, '(i3)', advance='no') i
    end do
    print *

    do i = 5, 1, -1
        write (*, '(i3)', advance='no') i
    end do
    print *
end program dostep

The first loop prints the six even numbers from 0 to 10, and the second counts down from 5 to 1. The write statements use advance='no' so that the values appear on one line, and the empty print * ends the line. A negative step with a start smaller than the end runs zero times, exactly as a positive step with a start greater than the end does.

$ gfortran dostep.f90 -o dostep
$ ./dostep
  0  2  4  6  8 10
  5  4  3  2  1

Zero-trip loops and fixed bounds

A counted loop whose bounds are already past each other runs zero times, which is well defined and not an error. The bounds are also evaluated once, before the first iteration, and not re-read on every pass, so changing a bound inside the body has no effect on the loop.

bounds.f90
program bounds
    implicit none
    integer :: i, n, first, last, count

    count = 0
    first = 1
    last = 0
    do i = first, last
        count = count + 1
    end do
    print *, 'iterations of 1 to 0:', count

    n = 3
    do i = 1, n
        n = 1
        write (*, '(i3)', advance='no') i
    end do
    print *
end program bounds

The first loop runs zero times, so the count stays 0. The second loop shows that the bound n is read once: although the body sets n to 1 during the first iteration, the loop still runs three times. Writing the bound into the body has no effect on the loop, which is another difference from the while form.

$ gfortran bounds.f90 -o bounds
$ ./bounds
 iterations of 1 to 0:           0
  1  2  3

The loop variable after the loop

The counter is an ordinary variable and keeps its last value when the loop ends. After a normal completion that value is one step beyond the end, because the counter is incremented once more before the test fails. After an exit the counter holds the value of the iteration that left the loop. Programs should not rely on either value, but the difference explains many surprises when a counter is printed after the loop.

after.f90
program after
    implicit none
    integer :: i

    do i = 1, 5
    end do
    print *, 'after normal completion:', i

    do i = 1, 5
        if (i == 2) exit
    end do
    print *, 'after exit at 2:        ', i
end program after

The first loop has an empty body and finishes normally, so the counter is 6. The second loop leaves at the iteration with i = 2, so the counter is still 2. The example is artificial, but a program that prints the counter after a loop does exactly this, and the answer depends on how the loop ended.

$ gfortran after.f90 -o after
$ ./after
 after normal completion:           6
 after exit at 2:                   2

do while

do while (condition) tests the condition before every iteration and stops when it becomes false. The loop must contain a statement that can change the condition, or it never ends. A do while is the right choice when the number of iterations is not known in advance, as in the Collatz sequence below.

dowhile.f90
program dowhile
    implicit none
    integer :: n, steps

    n = 27
    steps = 0
    do while (n /= 1)
        if (mod(n, 2) == 0) then
            n = n / 2
        else
            n = 3 * n + 1
        end if
        steps = steps + 1
    end do
    print *, 'reached 1 in', steps, 'steps'
end program dowhile

The Collatz rule halves an even number and replaces an odd one with three times itself plus one. Starting from 27 the sequence reaches 1 after 111 steps, and the loop stops at that point because the condition n /= 1 becomes false. The counter steps is incremented inside the body; the loop condition does not mention it.

$ gfortran dowhile.f90 -o dowhile
$ ./dowhile
 reached 1 in         111 steps

Endless do and exit

A do without a condition repeats forever until an exit statement leaves it. This form is useful when the stopping condition is naturally checked in the middle of the body, after some work has been done. The exit may appear anywhere in the loop body, including inside an if construct.

endless.f90
program endless
    implicit none
    integer :: n

    n = 0
    do
        n = n + 1
        if (n * n > 50) exit
    end do
    print *, 'first square above 50:', n * n, 'at n =', n
end program endless

The loop increments n and leaves as soon as its square exceeds 50. The first such square is 64, which belongs to 8, so the program prints those values. The do and the end do are the whole construct; there is no condition to write, and the compiler does not complain about a loop that could run forever.

$ gfortran endless.f90 -o endless
$ ./endless
 first square above 50:          64 at n =           8

Skipping with cycle

cycle abandons the current iteration and continues with the next one. In a counted loop it advances the counter as usual, so the loop still terminates. A common use is to skip values that do not need processing, which keeps the body flat instead of wrapping the work in an if.

cycle1.f90
program cycle1
    implicit none
    integer :: i

    do i = 1, 10
        if (mod(i, 2) == 0) cycle
        write (*, '(i3)', advance='no') i
    end do
    print *
end program cycle1

Even values trigger cycle and never reach the write, so the output contains 1, 3, 5, 7 and 9. The condition is tested at the top of the body, but cycle works from any depth, and a loop may contain several of them. The exit statement does the opposite: it ends the whole loop and continues after end do.

$ gfortran cycle1.f90 -o cycle1
$ ./cycle1
  1  3  5  7  9

Named loops

A loop may be given a name, written before do and after end do. The name makes it possible for an inner loop to exit or cycle an outer loop, which a bare statement cannot do. Without the name, exit always leaves the innermost loop.

named.f90
program named
    implicit none
    integer :: i, j

    outer: do i = 1, 3
        do j = 1, 3
            if (i * j == 6) exit outer
            print *, i, j
        end do
    end do outer
end program named

The loop is called outer, and the exit outer statement leaves both loops at once. The first product equal to 6 appears at i = 2 and j = 3, so five pairs are printed before the program stops. Replacing the named exit with a plain exit would leave only the inner loop and continue with i = 3.

$ gfortran named.f90 -o named
$ ./named
           1           1
           1           2
           1           3
           2           1
           2           2

Nested loops

Loops nest in the obvious way: the inner loop completes all its iterations for each single iteration of the outer one. The total number of executions of the body is the product of the two counts. Indentation is the only thing that shows the structure, since every loop ends with the same end do keyword.

nested.f90
program nested
    implicit none
    integer :: i, j

    do i = 1, 3
        do j = 1, 3
            write (*, '(i2, a, i2, a, i3)', advance='no') i, ' x ', j, ' = ', i * j
        end do
        print *
    end do
end program nested

The inner loop prints three products on a line, and the outer loop ends each line with an empty print. The format (i2, a, i2, a, i3) mixes integers and character constants, so the whole product line is written by one statement without extra spacing. Nine products appear in three rows of three.

$ gfortran nested.f90 -o nested
$ ./nested
 1 x  1 =   1 1 x  2 =   2 1 x  3 =   3
 2 x  1 =   2 2 x  2 =   4 2 x  3 =   6
 3 x  1 =   3 3 x  2 =   6 3 x  3 =   9

The implied do

Inside an input or output list, and inside an array constructor, a set of values can be generated by an implied do: a parenthesised list with a counter clause. It is not a statement and has no end do, so it cannot contain arbitrary statements. The construct is the usual way to print an array in one statement or to build an array from a formula.

implied.f90
program implied
    implicit none
    integer :: i, total

    print *, (i * i, i = 1, 5)
    total = sum([(i * i, i = 1, 5)])
    print *, 'sum:', total
    print '(5i4)', (i * i, i = 1, 5)
end program implied

The first statement prints the five squares through list-directed output, which adds its own spacing. The second uses the same construct inside an array constructor, so [ ... ] builds an array of five elements and sum adds them to 55. The last line formats the same values with an explicit repeat count, which gives a narrower and more even layout. Implied dos may also be nested, which is how a matrix is printed row by row.

$ gfortran implied.f90 -o implied
$ ./implied
           1           4           9          16          25
 sum:          55
   1   4   9  16  25

do concurrent

Fortran 2008 added do concurrent, a counted loop whose iterations the compiler may run in any order or in parallel. The body may not branch out of the loop or share a variable between iterations, so the result does not depend on the order. On a serial build it behaves like an ordinary loop, and this article mentions it only to name it; the parallel article covers it in detail.

docon.f90
program docon
    implicit none
    integer :: i
    real :: a(5)

    do concurrent (i = 1:5)
        a(i) = i * 2.0
    end do
    print *, a
end program docon

Each iteration writes a different element of the array, so the iterations are independent and the compiler is free to reorder them. The array is printed in one statement, which is the whole-array style that the arrays article describes. Replacing the body with something that accumulates into a single variable would make the loop invalid.

$ gfortran docon.f90 -o docon
$ ./docon
   2.00000000       4.00000000       6.00000000       8.00000000       10.0000000    

A complete example

The program below finds the prime numbers up to 50. The outer loop visits every candidate, and the inner loop tests the divisors from 2 to half the candidate. An exit leaves the inner loop as soon as a divisor is found, and a logical flag records the result for the outer loop.

primes.f90
program primes
    implicit none
    integer :: n, d, count
    logical :: is_prime

    count = 0
    do n = 2, 50
        is_prime = .true.
        do d = 2, n / 2
            if (mod(n, d) == 0) then
                is_prime = .false.
                exit
            end if
        end do
        if (is_prime) then
            write (*, '(i4)', advance='no') n
            count = count + 1
        end if
    end do
    print *
    print *, count, 'primes below 51'
end program primes

The flag starts true for every candidate and is cleared when a divisor divides it exactly. The inner loop stops early through exit, which is what makes the program fast enough to be worth writing this way; a prime has no divisor and the loop runs to n / 2. Fifteen numbers survive, from 2 to 47, and the counter reports them. The values are written without a line break, so they share one line, and the bare print * ends it.

$ gfortran primes.f90 -o primes
$ ./primes
   2   3   5   7  11  13  17  19  23  29  31  37  41  43  47
          15 primes below 51

Next steps

Fortran conditionals describes the tests that these loops rely on, and Fortran intrinsic numeric functions covers the arithmetic used inside the bodies. Whole-array operations, which often replace a loop, are the subject of the Fortran arrays article.

Source

GNU Fortran: DO construct

GNU Fortran: DO CONCURRENT

Fortran 2018 standard (J3/18-007r1)

In this article we have counted with do, changed the step and counted downwards, skipped iterations with cycle, left loops with exit, named loops to leave several at once, nested loops, used the implied do, met do concurrent, and written a prime number program. The previous article, Fortran conditionals, describes the branches inside these loops.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Fortran tutorials.