Errors: How Much Can We Trust a Simulation?

Module 5.2 · Errors

TipThe question

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?

NoteCode and reading

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.

Meeting 1: Find and measure error

NoteVideo openers: seeing and explaining a failure

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.

Start with a real failure

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.

How large is an error?

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.

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

Which measure is easier to interpret if the number represents meters? Which helps compare errors for values measured on very different scales?

ImportantA reference value is not always available

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.

Why does 0.1 + 0.2 surprise us?

Predict whether the final line is TRUE or FALSE:

0.1 + 0.2
[1] 0.3
0.3
[1] 0.3
0.1 + 0.2 == 0.3
[1] FALSE

R prints a rounded display. Ask it to show more digits:

sprintf("%.17f", 0.1 + 0.2)
[1] "0.30000000000000004"
sprintf("%.17f", 0.3)
[1] "0.29999999999999999"
(0.1 + 0.2) - 0.3
[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:

tolerance <- 1e-12
abs((0.1 + 0.2) - 0.3) < tolerance
[1] TRUE

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.

Does the order of subtraction matter?

Try these two expressions before reading the output. In exact arithmetic, both are zero. R evaluates each one from left to right:

9.1 - 9 - 0.1
[1] -3.608225e-16
9.1 - 0.1 - 9
[1] 0

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?

Can tiny errors accumulate?

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.

number_of_steps <- 100000
delta_t <- 0.1

time_by_addition <- 0
for (i in 1:number_of_steps) {
  time_by_addition <- time_by_addition + delta_t
}

time_from_step_number <- number_of_steps * delta_t
time_by_addition
[1] 10000
time_from_step_number
[1] 10000
time_by_addition - time_from_step_number
[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.

TipBefore leaving today

Classify each as data, model, implementation, or numerical error:

  1. A field sensor was never calibrated.
  2. A simulation uses a one-day time step and misses a rapid peak.
  3. A calculation uses kilometers where the next line expects meters.
  4. A disease model assumes everyone meets everyone equally often.

Which problem could be reduced by making the time step smaller? Which ones could not?

Meeting 2: Test the limits of a computation

Finite range: overflow and underflow

A computer stores numbers within a finite range. Predict the two results:

1e308 * 10
[1] Inf
1e-300 / 1e100
[1] 0

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.

Order of arithmetic can matter

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:

a <- 1e16
b <- -1e16
c <- 1

(a + b) + c
[1] 1
a + (b + c)
[1] 0

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.

A finite-step simulation is also an approximation

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
comparison
  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.

NoteThe book’s “truncation error” example

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.

What would make you trust a result more?

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?

WarningTakeaway

“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.

Back to top