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
PLATEmigration; Jacobi vs Gauss–Seidel). - Q14 → §18.1 step 1 (what adding
implicit noneactually 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.