Exercises: Variables, Types, and Arithmetic
This is the first chapter where the exercises are mostly code. Predict before you run — the whole point of
a "type, compile, and run" problem is the gap between what you expected and what the compiler did, and that
gap is where the learning lives. Compile everything with gfortran -std=f2018 -Wall; add -fcheck=all
while you are still finding your feet.
Difficulty: ⭐ warm-up · ⭐⭐ standard · ⭐⭐⭐ deeper. Solutions: worked solutions to the daggered (†)
and odd-numbered problems are in appendices/answers-to-selected.md; the compilable ones are in
code/exercise-solutions.f90. Try every problem before you look.
Part A — Warm-ups ⭐
3.1 † Name the six intrinsic types, and for each give a one-phrase example of a quantity you would store in it (from a simulation, if you can).
3.2 State the value and the type of each expression: 9 / 4, 9.0_dp / 4, 9 * 4, 2 ** 5,
mod(9, 4).
3.3 † In your own words, why does this book declare physical quantities as real(dp) rather than plain
real? What could go wrong with plain real in a long simulation?
3.4 What does selected_real_kind(15, 307) ask for, and what does it return? Why is this preferable to
writing real(8)?
3.5 † Without running it, what does precision(1.0) return, and what does precision(1.0_dp) return, on
a typical compiler? What is the practical consequence of the difference?
Part B — Type, Compile, and Run ⭐⭐
Predict the exact output first — write it down — then compile and run to check.
3.6 †
print '(a, i0)', 'q = ', 17 / 5
print '(a, i0)', 'r = ', mod(17, 5)
3.7
integer, parameter :: dp = selected_real_kind(15, 307)
print '(f8.4)', 22.0_dp / 7.0_dp
3.8 † Predict all three lines:
print '(a, i0)', 'a = ', mod(-8, 3)
print '(a, i0)', 'b = ', modulo(-8, 3)
print '(a, i0)', 'c = ', mod(8, -3)
3.9
integer, parameter :: dp = selected_real_kind(15, 307)
print '(a, f8.3)', 'x = ', 2.0_dp ** 0.5_dp
print '(a, f8.3)', 'y = ', abs(-6.25_dp)
3.10 † What does this print, and why is the second line not 1.500?
integer, parameter :: dp = selected_real_kind(15, 307)
real(dp) :: p
p = 3 / 2
print '(a, f6.3)', 'p (from 3/2) = ', p
p = 3.0_dp / 2.0_dp
print '(a, f6.3)', 'p (from 3.0/2.0) = ', p
Part C — Find the Bug ⭐⭐
Each snippet compiles (unless noted) but is wrong, or fails to compile for an instructive reason. Diagnose and fix it.
3.11 † Meant to compute the average of three integer readings as a real:
real(dp) :: avg
avg = (10 + 15 + 21) / 3
3.12 Meant to give g the full double-precision value of 9.81:
real(dp) :: g = 9.81
3.13 † Meant to compute $\sin(30^\circ)$:
real(dp) :: s
s = sin(30.0_dp)
3.14 This will not compile. Why?
real(dp), parameter :: pi = 3.141592653589793_dp
pi = 3.14_dp
3.15 † Meant to test whether a step count is even:
integer :: n = 7
logical :: even
even = mod(n, 2)
Part D — Port It ⭐⭐
Translate each snippet to correct, modern Fortran (implicit none, real(dp), _dp literals). Mind the
arithmetic semantics — they are not always the same across languages.
3.16 † From Python. Note that Python's / is real division; make the Fortran give the same answer.
f = 98.6
c = 5 / 9 * (f - 32)
print(c) # -> 37.0
3.17 From Python. Beware: Python's // floors, Fortran's integer / truncates toward zero. Predict
where they differ, then write the Fortran and say what it prints.
print(-7 // 2) # -> -4 in Python
3.18 † From MATLAB. Compute the Euclidean distance between the points $(1, 2)$ and $(4, 6)$.
d = sqrt((4-1)^2 + (6-2)^2) % -> 5
3.19 From Python. Port this and report the printed value.
import math
print(math.hypot(3.0, 4.0)) # -> 5.0
Part E — Back of the Envelope ⭐⭐⭐
Order-of-magnitude estimates; show your reasoning.
3.20 † The heat solver stores the plate temperature in a 1000 × 1000 array of real(dp). How many
megabytes does that array occupy? How much would it occupy in single precision? (Take 1 MB = $10^6$ bytes.)
3.21 A default 32-bit integer spans roughly $\pm 2.1 \times 10^9$. You need to count events in a
simulation that produces about $5 \times 10^{9}$ of them. Will a default integer do? If not, what
selected_int_kind(r) request gives you a type that will, and roughly what range does it cover?
3.22 † Single precision carries about 7 significant decimal digits; double carries about 15. If a
simulation loses roughly one digit of accuracy per $10^{6}$ arithmetic operations to accumulated rounding,
after how many operations does single precision have essentially no correct digits left? Why does this make
real(dp) the safe default for a long run?
Part F — Design It: Extend the Solver ⭐⭐
3.23 † Extend the Project Checkpoint's checkpoint program. Add a real(dp), parameter for the plate's
initial interior temperature t_init = 20.0_dp (°C) and its hot-edge temperature t_hot = 100.0_dp (°C),
and print both with an f8.3 descriptor. Predict the output before running.
3.24 Still in the checkpoint program, compute and print the number of interior grid points (not counting
the fixed boundary), which for an nx × nx grid is (nx - 2) ** 2. For nx = 101, what is it? Use i0.
3.25 † Add a derived real(dp), parameter :: t_diffuse = length**2 / alpha — a rough diffusion timescale
in seconds — and print it with es10.3. With length = 1.0_dp and alpha = 1.0e-4_dp, what do you get, and
what does it tell you about how long the simulation must run? (The rigorous stability story waits for
Chapter 24.)
Part G — Interleaved ⭐⭐
These reach back to earlier chapters.
3.26 † (Chapter 2.) Write the full gfortran
command you would use to compile checkpoint.f90 into an executable named run, with all warnings on,
standard set to Fortran 2018, and run-time checks enabled for development. Which flag would you remove for
a production build, and why?
3.27 (Chapter 1.) The chapter's Performance Note argued that single precision can be faster than double. Connect this to Chapter 1's claim that "performance is not accidental": which property of the hardware makes halving the bytes speed up a memory-bound loop?
3.28 † (Chapter 2.) Your checkpoint program uses
implicit none. A colleague's version omits it and mistypes alpha as alhpa in one place. Describe
exactly what happens in each case (with and without implicit none), and which one you would rather debug.
Solutions to the daggered (†) and odd-numbered problems are in appendices/answers-to-selected.md; the
compilable numeric solutions (3.16, 3.18, 3.13, and the quadratic of the discussion) are collected in
code/exercise-solutions.f90. The estimation problems (3.20–3.22) give a worked order-of-magnitude answer
rather than a single exact number.