Beginner

Control Flow in Fortran

Learn conditionals, select case, and counted and conditional loops in Fortran with runnable Docker examples

Control flow is what turns a list of statements into a program that makes decisions and repeats work. As a structured, imperative language, Fortran offers exactly the constructs you would expect: if for branching, select case for multi-way selection, and the versatile do loop for iteration. What makes Fortran distinctive is its long history — these constructs were refined across decades, so the modern free-form syntax is clean and block-structured, while older code you encounter may still use arithmetic IF statements and numbered GOTO labels.

This tutorial uses modern Fortran (Fortran 90 and later) exclusively. Every block construct has an explicit terminator — end if, end select, end do — which keeps deeply nested logic readable and lets the compiler catch mismatched blocks. Fortran is case-insensitive, so IF and if are identical; we use lowercase by modern convention.

You will learn how to branch with if...else if...else, select among many values with select case, count with the do loop (including custom strides), loop on a condition with do while, and control loop execution with cycle and exit. Every example begins with implicit none, the modern best practice that forces explicit variable declarations.

Conditionals: if, else if, else

The if construct evaluates a logical condition in parentheses. If true, the block between then and the next branch runs. You can chain alternatives with else if and provide a fallback with else. The block form always ends with end if.

Fortran’s relational operators are ==, /= (not equal), <, >, <=, and >=. The older FORTRAN 77 spellings .EQ., .NE., .LT., .GT., .LE., and .GE. still work and appear frequently in legacy code. Logical operators are .and., .or., and .not..

Create a file named if_else.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
program if_else
    implicit none
    integer :: score

    score = 85

    if (score >= 90) then
        print *, "Grade: A"
    else if (score >= 80) then
        print *, "Grade: B"
    else if (score >= 70) then
        print *, "Grade: C"
    else
        print *, "Grade: F (failing)"
    end if

    ! A single-line if has no "then" and no "end if"
    if (score >= 60) print *, "Result: Passed"
end program if_else

The chained else if ladder picks the first matching branch and skips the rest, so score = 85 prints Grade: B. The final statement shows the one-line if: when only a single statement follows the condition, you omit then and end if entirely.

Multi-way Selection: select case

When you are comparing one variable against many discrete values, a select case construct is clearer than a long if ladder. Each case lists one or more values; select case jumps directly to the matching branch. Unlike C, there is no fall-through — exactly one branch runs, and no break is needed.

A case can match a single value case (1), a list case (6, 7), or a range case (2:5). The optional case default handles anything unmatched. select case works with integers, characters, and logicals.

Create a file named select_case.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
program select_case
    implicit none
    integer :: day
    character(len=1) :: grade

    day = 3
    select case (day)
    case (1)
        print *, "Monday"
    case (2:5)
        print *, "A midweek day"
    case (6, 7)
        print *, "Weekend!"
    case default
        print *, "Invalid day"
    end select

    grade = "B"
    select case (grade)
    case ("A", "B")
        print *, "Excellent work"
    case ("C", "D")
        print *, "Room to improve"
    case default
        print *, "See me after class"
    end select
end program select_case

With day = 3, the value falls inside the 2:5 range, so it prints A midweek day. The second select case shows that character values work too: grade = "B" matches the ("A", "B") list.

Counted and Conditional Loops: do

The do loop is Fortran’s primary iteration construct. The counted form takes a loop variable, a start, and an end: do i = 1, 5. An optional third value sets the stride, which may be negative for counting down. The body runs until the loop variable passes the end value, and the block closes with end do.

For loops that run until a condition changes — rather than a fixed count — use do while (condition). The condition is tested before each iteration, so the body may run zero times.

Create a file named do_loops.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
program do_loops
    implicit none
    integer :: i
    integer :: total
    integer :: n

    ! Counted DO loop: sum the integers 1 through 5
    total = 0
    do i = 1, 5
        total = total + i
    end do
    write(*, '(A, I0)') "Sum of 1..5 = ", total

    ! DO loop with a stride of -2 (count down)
    do i = 10, 2, -2
        write(*, '(A, I0)') "Countdown: ", i
    end do

    ! DO WHILE loop: halve until the value drops to 1
    n = 100
    do while (n > 1)
        n = n / 2
        write(*, '(A, I0)') "n = ", n
    end do
end program do_loops

The first loop accumulates 1 + 2 + 3 + 4 + 5 = 15. The second uses a stride of -2 to step from 10 down to 2. The do while loop repeatedly applies integer division (which truncates toward zero), so 100 becomes 50, 25, 12, 6, 3, 1. Here we use write with the I0 format descriptor, which prints an integer in its minimum width with no leading blanks — handy when mixing numbers and text on one line.

Loop Control: cycle and exit

Two keywords alter loop execution. cycle skips the rest of the current iteration and moves to the next (like continue in C). exit leaves the loop immediately (like break). Both default to the innermost loop, but Fortran lets you name a loop and target it explicitly — invaluable for breaking out of nested loops.

The mod(a, b) intrinsic returns the remainder of a divided by b, which is the standard way to test for even or odd values.

Create a file named loop_control.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
program loop_control
    implicit none
    integer :: i, j

    ! CYCLE skips to the next iteration (like "continue")
    do i = 1, 6
        if (mod(i, 2) == 0) cycle   ! skip even numbers
        write(*, '(A, I0)') "Odd: ", i
    end do

    ! EXIT leaves the loop early (like "break")
    do i = 1, 100
        if (i > 3) exit
        write(*, '(A, I0)') "Counting: ", i
    end do

    ! Named loops let EXIT target a specific outer loop
    outer: do i = 1, 3
        inner: do j = 1, 3
            if (i + j == 4) exit outer
            write(*, '(A, I0, A, I0)') "i=", i, " j=", j
        end do inner
    end do outer
end program loop_control

The first loop prints only odd numbers because cycle skips even ones. The second stops the moment i exceeds 3, even though the range goes to 100. The named loops are the key trick: exit outer breaks out of both loops at once when i + j first reaches 4, which happens at i=1, j=3 — so the last printed pair is i=1 j=2.

Running with Docker

Each file is a complete, standalone Fortran program. Use the official GCC image, which includes gfortran:

1
2
3
4
5
6
7
8
# Pull the official GCC image (includes gfortran)
docker pull gcc:latest

# Compile and run each example
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o if_else if_else.f90 && ./if_else'
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o select_case select_case.f90 && ./select_case'
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o do_loops do_loops.f90 && ./do_loops'
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o loop_control loop_control.f90 && ./loop_control'

Expected Output

if_else.f90:

 Grade: B
 Result: Passed

select_case.f90:

 A midweek day
 Excellent work

do_loops.f90:

Sum of 1..5 = 15
Countdown: 10
Countdown: 8
Countdown: 6
Countdown: 4
Countdown: 2
n = 50
n = 25
n = 12
n = 6
n = 3
n = 1

loop_control.f90:

Odd: 1
Odd: 3
Odd: 5
Counting: 1
Counting: 2
Counting: 3
i=1 j=1
i=1 j=2

Note that the print * statements in the first two examples produce a leading space (Fortran’s list-directed carriage-control blank, seen in Hello World), while the write(*, '(A, I0)') statements use an explicit format and start at the first column with no leading space.

Key Concepts

  • Block constructs have explicit terminatorsend if, end select, and end do close their blocks, making nested control flow easy to read and letting the compiler catch mismatches.
  • if...else if...else picks the first true branch — chain alternatives with else if; use the one-line if (cond) statement form (no then, no end if) for a single action.
  • select case has no fall-through — exactly one branch runs and no break is needed; cases match single values, lists like (6, 7), or ranges like (2:5).
  • The do loop counts; an optional third value sets the stridedo i = 10, 2, -2 counts down, and the loop variable, start, and end can be any integer expressions.
  • do while tests before each iteration — use it when the iteration count depends on a condition rather than a fixed range; it may run zero times.
  • cycle skips an iteration; exit leaves the loop — they behave like continue and break, and both default to the innermost loop.
  • Named loops give exit and cycle a target — labeling a loop (outer: do ...) lets you break out of a specific enclosing loop from inside nested loops.
  • Relational operators come in two spellings — modern ==, /=, <, >, <=, >= and the FORTRAN 77 forms .EQ., .NE., .LT., .GT., .LE., .GE. are interchangeable.

Running Today

All examples can be run using Docker:

docker pull gcc:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining