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.