reference <- 6.239
approximation <- 6.24
absolute_error <- abs(approximation - reference)
relative_error <- absolute_error / abs(reference)
absolute_error[1] 0.001
100 * relative_error # percent[1] 0.01602821
Module 5.2 · Errors
Our R code can run without an error message and still give a bad answer. Where can an error enter a simulation, how can we measure it, and when should it change our conclusion?
Download the complete R script after coding along. Read Module 5.2, “Errors,” in Introduction to Computational Science. This is one textbook module; we will work through it over two class meetings. Today we focus on sources of error, measuring error, and floating-point arithmetic. The continuation is ready below.
First, watch ESA’s 4½-minute footage of the Ariane 501 launch failure. It shows the event but does not explain the cause. What evidence would we need to find out what went wrong?
Then watch Engineering Disasters 13 — Software Flaws, the excerpt about Ariane 5 and the Patriot missile system. As you watch, note what went wrong in each case and whether the two failures had the same cause.
NASA’s Mars Climate Orbiter was lost after ground software used English units while onboard software used metric units. Before we discuss any floating-point arithmetic, ask: Would a more precise computer have fixed that problem? Why or why not?
Errors can enter at different stages:
| Type | Example | What to check |
|---|---|---|
| Data | An instrument reports the wrong temperature | Calibration and source quality |
| Model | We omit an important way a disease spreads | Assumptions and validation against observations |
| Implementation | One program supplies pounds while another expects newtons | Code, units, and interface tests |
| Numerical | A finite time step approximates continuous change | Precision and sensitivity to the method or step size |
The last category is our main coding focus, but fixing numerical error cannot repair a wrong model or incorrect units.
Suppose the reference value is \(6.239\), but we report \(6.24\). Predict the difference before running R.
\[ \text{absolute error}=|\text{approximation}-\text{reference}|, \qquad \text{relative error}=\frac{\text{absolute error}}{|\text{reference}|}. \]
The relative error is undefined when the reference value is zero. Multiply it by 100 to express it as a percentage.
[1] 0.001
[1] 0.01602821
Which measure is easier to interpret if the number represents meters? Which helps compare errors for values measured on very different scales?
In a real modeling problem, we often do not know the true answer. We can compare against a measurement, an analytical solution, or a more refined computation, but must say which reference we used. A small difference from one reference is not proof that our model describes reality.
0.1 + 0.2 surprise us?Predict whether the final line is TRUE or FALSE:
R prints a rounded display. Ask it to show more digits:
[1] "0.30000000000000004"
[1] "0.29999999999999999"
[1] 5.551115e-17
Most decimal fractions cannot be represented exactly with a finite number of binary digits. The computer stores nearby values; the difference here is tiny, but exact equality sees it.
When our question is whether two computed values are close enough, choose a tolerance appropriate to the scale of the problem:
This tolerance is suitable for this small arithmetic example; it is not a universal threshold for every model or unit. R also provides all.equal() for approximate comparisons.
Try these two expressions before reading the output. In exact arithmetic, both are zero. R evaluates each one from left to right:
On our R setup, the first leaves a tiny negative remainder (about \(-3.6\times10^{-16}\)), while the second gives exactly zero. The intermediate calculations round differently, even though the expressions are algebraically equivalent. Which error measure can we use when the correct answer is zero: absolute or relative error? Would either result matter if these were measurements in meters?
Our simulations have used a vector of times made with seq(). What might happen if we repeatedly add \(0.1\) instead? Predict whether both methods below give exactly 10,000.
[1] 10000
[1] 10000
[1] 1.884837e-08
The accumulated difference is small here. The general lesson is to measure the effect, not to assume every floating-point difference matters. When time is determined by the step number, calculating it from that number avoids repeated addition of the time step.
Classify each as data, model, implementation, or numerical error:
Which problem could be reduced by making the time step smaller? Which ones could not?
A computer stores numbers within a finite range. Predict the two results:
In ordinary R numeric arithmetic, the first becomes Inf (too large) and the second becomes 0 (too small to represent here). Both should trigger questions if they appear unexpectedly in a simulation. In ESA’s account of Ariane 5 Flight 501, a value exceeded the capacity of a converted representation; that was not the same mechanism as 0.1 + 0.2.
Our \(9.1\) subtraction produced a tiny discrepancy. Mathematically, \((a+b)+c=a+(b+c)\) too—but finite precision can make the difference much larger. Predict these R results:
Why can the 1 be lost in one order? This is another consequence of finite precision. The textbook also discusses adding small quantities before large ones when feasible.
For the Module 2.2 example, \(dP/dt=0.1P\), \(P(0)=100\), and \(t=8\) hours. We know the analytical value \(100e^{0.8}\). Repeating the Euler update for a step of length \(\Delta t\) gives
\[ P_{Euler}(8)=100(1+0.1\Delta t)^{8/\Delta t}. \]
Before running the code, predict what happens to the error as we shorten the step:
delta_t_values <- c(1, 0.5, 0.1, 0.01)
exact_population <- 100 * exp(0.1 * 8)
euler_population <- 100 *
(1 + 0.1 * delta_t_values)^(8 / delta_t_values)
comparison <- data.frame(
delta_t = delta_t_values,
euler_population = euler_population,
absolute_error = abs(euler_population - exact_population)
)
exact_population[1] 222.5541
delta_t euler_population absolute_error
1 1.00 214.3589 8.19521185
2 0.50 218.2875 4.26663401
3 0.10 221.6715 0.88257113
4 0.01 222.4651 0.08894456
The error here mostly comes from approximating continuous change with finite steps, not from the tiny storage error in 0.1. We saw this in Module 2.2; now we have names and measurements for it.
Module 5.2 demonstrates truncation error by stopping an infinite series after finitely many terms, such as \(e^x=1+x+x^2/2!+\cdots\). A finite Euler step is a related approximation of continuous mathematics, often called discretization or truncation error in numerical methods. Do not confuse either with round-off error, which comes from finite storage of numbers.
For one of our growth, predator–prey, or SIR simulations, propose three checks. Consider units, conservation or other invariants, a different time step, and whether the assumptions fit the question. What would each check reveal—and what could it not reveal?
“The code ran” is only a starting point. Name the source of possible error, estimate its size when possible, and decide whether it changes the conclusion you are willing to draw.