Self-Assessment Quiz: Modernizing Legacy Fortran

Twenty questions to confirm you can modernize old Fortran safely and prove you did not break it. Aim for 16 or more. Answers and a topic map are at the end — try the whole quiz first.


Question 1

Which is the recommended first step of the eight-step modernization recipe? - A. Convert COMMON blocks to modules - B. Add implicit none - C. Add error handling - D. Replace GOTO with do loops

Question 2

The single step that does the most for a legacy code's reliability is generally: - A. Reindenting the source - B. Replacing COMMON blocks with modules - C. Renaming variables - D. Adding comments

Question 3

"Incremental modernization" means: - A. Rewriting the whole program in one pass, then testing at the end - B. Making small, individually verified changes, keeping the code correct at every step - C. Only modernizing the parts you have time for - D. Adding features while you modernize

Question 4

A refactoring is a change that: - A. Improves the code's behavior - B. Adds a new feature - C. Alters the form of the code while leaving its observable behavior unchanged - D. Always makes the code faster

Question 5

A characterization test is written to: - A. Specify what the code should do - B. Capture what the code currently does, so you can refactor underneath it - C. Measure performance - D. Document the code's authors

Question 6

You may legitimately demand bit-for-bit agreement between the legacy and modern versions when: - A. You changed the precision from single to double - B. You enabled -ffast-math - C. Your changes preserved the arithmetic (same operations, order, and precision) - D. Never — floating point is never reproducible

Question 7

Which change makes bit-for-bit agreement impossible, requiring tolerance-based validation instead? - A. Converting fixed-form to free-form - B. Replacing a COMMON block with a module - C. Promoting real to real(dp) - D. Adding implicit none

Question 8

A legacy loop continues while DMAX .GT. TOL .AND. ITER .LT. MAXIT. The correct modern exit condition is: - A. if (dmax > tol .and. iters < maxit) exit - B. if (dmax <= tol .or. iters >= maxit) exit - C. if (dmax < tol .and. iters > maxit) exit - D. if (dmax >= tol .or. iters <= maxit) exit

Question 9

Why must a numerical regression test parse the numbers rather than run diff on the output text? - A. diff is too slow - B. Formatting differences (I5 vs i0, D vs E exponent, whitespace) are not numerical differences - C. Text files cannot be compared - D. The numbers are always identical anyway

Question 10

The modern replacement for a COMMON block is a: - A. GOTO - B. Module - C. BLOCK DATA - D. Statement function

Question 11

The recipe step that replaces EQUIVALENCE uses which modern tools, depending on the original purpose? - A. GOTO and arithmetic IF - B. Array intrinsics (e.g. maxval), the transfer intrinsic, or a derived type - C. COMMON and DATA - D. Implicit typing

Question 12

A statement function such as AVG(A,B,C,D) = 0.25*(A+B+C+D) is best modernized as: - A. A GOTO - B. An internal procedure (or inlined, if used once) - C. A COMMON block - D. An EQUIVALENCE

Question 13

On the 4×4 PLATE test grid (top edge 100°, three edges 0°), the converged interior is: - A. All 25° - B. Upper cells 37.5°, lower cells 12.5° - C. All 50° - D. Upper cells 75°, lower cells 25°

Question 14

True or false: "Adding implicit none to a legacy routine that relied on implicit typing will usually compile immediately with no further edits."

Question 15

intent(in) on a dummy argument does which of the following? - A. Lets the routine modify the argument freely - B. Documents and enforces that the routine only reads the argument, and helps the optimizer - C. Makes the argument optional - D. Converts the argument to a pointer

Question 16

"Bit-for-bit reproducible" is a property of: - A. The source code alone - B. The source code together with the compiler and its flags - C. The operating system only - D. The programmer's intentions

Question 17

The motto "never rewrite what you can refactor" is justified mainly because a rewrite: - A. Is always more expensive per line - B. Discards the irreplaceable validated behavior and re-earns it only at the very end, if ever - C. Produces uglier code - D. Cannot use modern features

Question 18

Modernizing assumed-size array arguments (a(*) / a(n)) to assumed-shape (a(:)) has which benefit? - A. The array carries its own shape, and run-time bounds checking becomes possible - B. It makes the code run in parallel automatically - C. It removes the need for implicit none - D. It converts the array to a scalar

Question 19

What does this modern loop print for iter?

integer :: iter
real :: r
r = 1.0; iter = 0
do
  iter = iter + 1
  r = 0.5 * r
  if (r <= 0.1 .or. iter >= 100) exit
end do
print '(i0)', iter
  • A. 3
  • B. 4
  • C. 7
  • D. 100

Question 20

A "modernization" of a Jacobi solver replaces the two-array update with an in-place update, silently turning it into Gauss–Seidel. Compared with the original, it: - A. Is a valid refactoring — same behavior - B. Changes the numerics (different iterates and iteration count), so it is not a pure refactoring - C. Cannot compile - D. Always produces a wrong steady state


Answer Key

Q Ans Why
1 B implicit none first — it forces declaration and understanding, and catches typos.
2 B Modules replace unchecked global memory overlay with typed, checked, named state.
3 B Small, verified steps; the code works at every step.
4 C A refactoring changes form, not observable behavior.
5 B It captures current behavior so you can refactor safely underneath it.
6 C Bit-for-bit is achievable exactly when the arithmetic is unchanged.
7 C Changing precision shifts the low-order bits — an improvement, but not bit-for-bit.
8 B The exit condition is the logical negation of the legacy continue condition.
9 B Format/whitespace differences are not numerical differences; parse the numbers.
10 B A module is the modern namespace that replaces COMMON.
11 B maxval/array intrinsics, transfer for bit reinterpretation, or a derived type.
12 B An internal procedure, or inline it when used once.
13 B 37.5° upper, 12.5° lower (from 3a−b=100, a=3b).
14 False It will fail to compile until you declare every variable — which is the point.
15 B It documents, enforces read-only, and aids optimization.
16 B Same source can differ across compilers/flags; pin them.
17 B The validated behavior is the irreplaceable asset a rewrite throws away.
18 A The array carries its shape; bounds checking becomes possible.
19 B r = 0.5, 0.25, 0.125, 0.0625; 0.0625 ≤ 0.1 first at iter 4.
20 B In-place update is Gauss–Seidel: different iterates and count — not a pure refactoring.

Topics to review by question

  • Q1–5, 17 → §18.1 and §18.2 (the recipe, its order, incremental modernization, refactoring).
  • Q6–9, 16 → §18.3 (regression tests, numerical equivalence, bit-for-bit vs tolerance).
  • Q10–12, 15, 18 → §18.1 (the individual steps and their modern replacements).
  • Q13, 20 → §18.4 (the worked PLATE migration; Jacobi vs Gauss–Seidel).
  • Q14 → §18.1 step 1 (what adding implicit none actually does).
  • Q19 → §18.1 step 6 and §18.4 (structured convergence loops; trace it by hand).

Scored below 16? Reread the flagged sections. The skills here — safe refactoring and proving equivalence — are exactly what a job maintaining scientific Fortran will ask of you.