Exercises: Control Flow
This is your first chapter where every exercise can be run. Do exactly that: predict the output on paper
first, then compile with gfortran -std=f2018 -Wall and check yourself. The gap between your prediction and
the machine's answer is where the learning is — and if they ever disagree and you are sure you are right,
you may have found a bug, which is a good day.
Difficulty: ⭐ warm-up · ⭐⭐ standard · ⭐⭐⭐ deeper. Solutions: worked solutions to the daggered (†)
and odd-numbered problems are in appendices/answers-to-selected.md; the four computational ones (4.7, 4.9,
4.18, 4.26) also ship as compilable code in code/exercise-solutions.f90. Try every problem before you look.
Part A — Warm-ups ⭐
4.1 † Predict the exact output of this snippet, then run it:
integer :: n = 7
if (n < 0) then
print '(a)', 'negative'
else if (n == 0) then
print '(a)', 'zero'
else if (n < 10) then
print '(a)', 'small positive'
else
print '(a)', 'large'
end if
4.2 List the six relational operators, and write how Fortran spells logical AND, OR, and NOT. How do these spellings differ from Python and from C?
4.3 † Give the two reasons the expression in a select case may be an integer or character but never
a real.
4.4 How many times does do i = 1, 10, 2 execute its body, and what values does i take? What about
do i = 10, 1, -3?
4.5 † In one sentence each, state the difference between exit and cycle, and between a bare exit and
exit outer (where outer names a loop).
4.6 True or false, with one sentence of justification: "A bare exit inside the inner of two nested
loops terminates both loops."
Part B — Type, Compile, and Run ⭐⭐
Predict the output first; then compile and run.
4.7 † Write a program that prints the numbers 1 through 15, one per line, except that multiples of 3 print
Fizz, multiples of 5 print Buzz, and multiples of 15 print FizzBuzz. (Which condition must you test
first, and why?)
4.8 Write a counted do loop that computes and prints $8!$ (eight factorial). Use an integer accumulator
initialized to 1. What is the largest factorial that fits in a default 32-bit integer, roughly — and how
would you find out? (Recall huge from Chapter 3.)
4.9 † Using a do while loop, count how many steps the Collatz sequence takes to reach 1 starting from
$n = 6$ (rule: if $n$ is even, $n \leftarrow n/2$; if odd, $n \leftarrow 3n+1$). Predict the count by hand,
then confirm.
4.10 Write a program that maps an integer 1–7 to a weekday name (1 -> Monday, …, 7 -> Sunday) with a
select case, and prints invalid via case default for anything else. Test it on 3, 7, and 9.
Part C — Port It ⭐⭐
Translate the given code to idiomatic modern Fortran. Match the output, then note one difference between the languages that the port made you confront.
4.11 † Port this Python loop:
total = 0
i = 1
while total < 100:
total += i
i += 1
print(i - 1, total)
4.12 Port this Python loop, which sums the numbers 1–20 but skips multiples of 4, using cycle:
s = 0
for k in range(1, 21):
if k % 4 == 0:
continue
s += k
print(s)
4.13 † Port this C switch, and explain what the deliberate missing break does — and how your Fortran
version, which cannot fall through, must be written to reproduce it:
switch (grade) {
case 'A':
case 'B': printf("pass with merit\n"); break;
case 'C': printf("pass\n"); break;
default: printf("see instructor\n");
}
Part D — Find the Bug ⭐⭐
Each compiles-or-runs wrongly. Diagnose it, then fix it.
4.14 † This is meant to stop when x reaches exactly 1.0, but it loops far longer (or forever). Why?
real(dp) :: x = 0.0_dp
do
x = x + 0.1_dp
if (x == 1.0_dp) exit
end do
4.15 This should stop scanning the entire table at the first zero, but it only stops the current row. Fix it.
do row = 1, nrows
do col = 1, ncols
if (a(col, row) == 0) exit
end do
end do
4.16 † This select case will not compile. Why, and how do you fix it?
select case (score)
case (0:60)
print '(a)', 'fail'
case (60:100)
print '(a)', 'pass'
end select
4.17 This do concurrent gives wrong or nondeterministic results when the compiler actually parallelizes
it. Explain, and rewrite it as an ordinary do loop.
do concurrent (i = 2:n)
a(i) = a(i) + a(i-1)
end do
Part E — Design It (Extend the Heat Solver) ⭐⭐
4.18 † Extend the Chapter 4 checkpoint. Before the time loop, add a select case on an
integer, parameter :: bc_type that prints which boundary condition is active — 1 Dirichlet, 2 Neumann,
3 periodic, anything else UNKNOWN. Loop bc_type over 0–3 to show all four branches.
4.19 Modify the skeleton so that, instead of reporting every step, it prints its boundary/interior counts
only every third step, using mod(step, 3) == 0 (with an if, or with a cycle). Run it with n_steps = 9
and predict which steps report.
4.20 † Add an early-exit to the time loop: introduce a logical :: converged (set it .true. when
step reaches some n_settle, a placeholder for the real convergence test of
Chapter 24), and exit the
named time loop when it becomes true. Print how many steps actually ran.
Part F — Back of the Envelope ⭐⭐⭐
Order-of-magnitude reasoning; show your work.
4.21 † The heat solver sweeps an $n \times n$ interior grid every time step, doing a fixed amount of work per interior point. For $n = 1000$ and $10^5$ time steps, roughly how many interior-point updates does a full run perform? At $10^{9}$ updates per second on one core, how long is that, and why does it push you toward Part VIII?
4.22 Suppose a 12-way dispatch is written as an if-chain of 12 equally-likely conditions tested in
order. On average, how many conditions are evaluated per call? If select case compiles instead to a single
jump table, how many? What does this say about the value of expressing intent precisely?
4.23 † A residual r starts at $r = 1.0$ and is halved each iteration of a do while (r > tol) loop.
About how many iterations run before it drops to tol $= 10^{-6}$? (Estimate with $\log_2$; you do not need
a computer.)
Part G — Interleaved (Chapters 2–3) ⭐⭐
4.24 † (Chapter 3 — integer division.) This loop is supposed to print the reciprocals $1/i$ as reals,
but prints 1.000 then all zeros. Diagnose and fix:
real(dp) :: r
integer :: i
do i = 1, 5
r = 1 / i
print '(f6.3)', r
end do
4.25 (Chapter 2 — flags.) Which gfortran flag makes an out-of-bounds array access inside a loop abort
with a helpful message instead of silently reading garbage, and why do you compile with it during
development but not in a production run?
4.26 † (Chapter 3 — logical operators and precedence.) A year is a leap year when it is divisible by 4
and not by 100, or it is divisible by 400. Write the single logical expression for leap, and evaluate it
by hand for 1900, 2000, 2024, and 2023. Does .and. bind tighter than .or.? Do you need parentheses?
4.27 (Chapter 3 — mixed mode.) Rewrite the condition if (n / 2 > 0.5) — where n is an integer —
so it does what the author almost certainly intended (is n at least 1?), and explain what the original
actually tests.
4.28 † Type, compile, and run code/project-checkpoint.f90. Then change nx, ny from 4, 3 to 5, 5
and, before recompiling, predict the new boundary= and interior= counts. Confirm.
Solutions to the daggered and odd-numbered problems are in appendices/answers-to-selected.md. The setup
problem 4.28 has an answer you produce at the keyboard; the appendix gives the predicted counts to check
against.