Decisions: if, else if, else
Comparing values, combining conditions with .and. and .or., and the block if that runs exactly one branch of a chain — plus why two real numbers should never be compared with ==.
Comparing values
A comparison is written with ==, /=, <, <=, >, >=, and its result is a logical: .true. or .false.. The one that catches people is ==. A single = assigns; two of them compare. An if written with one = is a compile error in modern Fortran, but only after you have stared at the line for a while.
Fortran will not let you write 1 < x < 10. Each comparison produces a logical, and comparing a logical with a number is not allowed, so the condition is written as two comparisons joined by .and..
Fortran also accepts the older spelled forms .eq., .ne., .lt., .le., .gt. and .ge.. They mean exactly what the symbols mean, and old numerical code is full of them. Mixing the two styles is legal, but a line that uses .eq. on one side and == on the other is harder to scan than either style alone; pick one for a file and keep it.
One block, one branch
An if block opens with if (condition) then and closes with end if. Between them sit the statements that run when the condition is true. Add else if (condition) then for further tests and else for the fallback, and the chain is read from the top: the first condition that is true wins, its branch runs, and every later branch is skipped.
That order matters. A chain that tests score >= 70 before score >= 90 gives a 95 the bronze grade, because the first true branch is the one that runs. Order the tests from the most specific to the most general.
There is also a one-line if for a single statement: if (score < 0) print *, 'invalid'. It has no then, no end if, and no else, so it is only for a statement that fits on the same line. The moment a second statement belongs to the branch, use the block form; converting later is easier than debugging a branch that only half ran.
Combining conditions
.and., .or. and .not. combine logical values, and ( ) groups them. Fortran does not promise to stop evaluating as soon as the answer is known, so a condition should be safe on both sides rather than relying on the left to protect the right.
Comparing reals is the other trap. 0.1 + 0.2 == 0.3 is false, because none of those three numbers is exactly representable in binary. Compare with a tolerance instead: abs(a - b) < 1.0e-6. The same caution applies to >= and <= on computed values.
A function call inside a condition is evaluated when the condition is reached, and Fortran does not promise to skip the right side of .and. when the left is false. That matters if the right side divides by a value that may be zero. Compute the guard into a logical first, or restructure the test so the dangerous expression is only reached when it is safe.
A grade, decided once
if (score >= 90) then
grade = 'gold'
else if (score >= 80) then
grade = 'silver'
else if (score >= 70) then
grade = 'bronze'
else
grade = 'none'
end ifA condition is a value
Fortran has no truthiness. if wants a logical value, so a comparison or a logical variable goes in the parentheses, and an integer does not: if (count) is a compile error where another language might have read zero as false. The upside is that a condition can be stored — logical :: ok then ok = score >= 60 gives the test a name you can print, pass, or combine.
Nesting is legal and indentation is the only thing that shows it. Each if inside another needs its own end if, and an else belongs to the nearest unmatched if above it. When a nested pair needs two else branches, a single flat chain joined with .and. is usually shorter and always easier to read.
Three slips break more branches than any others. if (condition) then needs the then; without it the compiler expects the whole branch on that one line. Every if needs its own end if, and adding an else if does not remove the need for it. And both sides of .and. must be complete comparisons — score >= 70 .and. < 90 is two half-conditions, so write the variable twice.
Try these next
- Move a score through 89, 90 and 91 and predict the grade before each run.
- Rewrite
if (score >= 80 .and. score < 90)using.not.and check it gives the same answer. - Store a comparison in a
logicalvariable, print it, and use it in anif.
Move the score, watch the branch
Run it, then change score to 95, 72 and 40 and run each time. Add one more branch of your own for a score below 10.
Two tests, one decision
Run it, then change attendance to 70 and score to 55 and run again. Add a third branch that prints something special for a score of exactly 100.
Never test a real with ==. Binary floating point cannot represent 0.1 exactly, so two values that a formula says are equal can differ in the last bit. Compare the difference against a small tolerance and say what "equal enough" means for the problem.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
Which operator compares two values for equality?
