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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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
List all Fortran tutorials.