Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Part 2: Floating-Point Arithmetic and Numerical Error

Module 1: Foundations of Discrete Computing | COMP 521

Mathematics treats real numbers as continuous and exact; computers store finite binary approximations. Numerical science begins when those worlds stop agreeing.

Learning Objectives

By the end of this section, you will be able to:


Why This Matters for Astronomy

Why this matters: astronomy forces us to compute across enormous ranges of scale, and floating-point arithmetic does not adapt its precision automatically to the physics we care about.

Student mental model: the computer does not store “the real number.” It stores the nearest representable binary value. That approximation is often excellent, but when we subtract nearly equal numbers, span many orders of magnitude, or iterate millions of times, the approximation can shape the science.

Astronomical simulations routinely cross very different physical scales:

If we work in CGS units, then these scales are

0.39 AU≈5.83×1012 cm,0.39\,\mathrm{AU} \approx 5.83\times 10^{12}\,\mathrm{cm},

which says that even an inner-planet orbit already lives at trillions of centimeters.

30 AU≈4.49×1014 cm,30\,\mathrm{AU} \approx 4.49\times 10^{14}\,\mathrm{cm},

which says that a solar-system calculation can span more than two orders of magnitude in position even before we consider velocities and forces.

10 pc≈3.09×1019 cm,10\,\mathrm{pc} \approx 3.09\times 10^{19}\,\mathrm{cm},

which says that star-formation problems mix local structure and cloud-scale structure in the same calculation.

Double precision gives about 15 to 16 decimal digits of relative precision near numbers of order unity. That is an approximate practical statement about floating-point spacing, not a law of nature about “how many digits exist” in a calculation.

Approximate astronomical lengths and their adjacent binary64 spacings are compared on logarithmic axes, both in centimeters.

Figure 1:The upper panel shows approximate length scales, all readily representable in binary64. The lower panel shows the spacing to the next larger binary64 value at each scale. These are different quantities: representing a large position does not imply resolving a very small displacement added to it. The cloud, galactic, and intergalactic lengths are illustrative scales, not measurements of particular objects.

Open full-size figure

Three number lines show adjacent floats near 1e-21, 1, and 1e16 using offsets in units of their local spacing.

Figure 2:What to notice in this figure: representable numbers are discrete rather than continuous, and the gap between adjacent floating-point values grows with magnitude. That is why a subtraction that is safe near 1 may be ill-conditioned near 1016.

Open full-size figure

The takeaway is that scale is part of the numerical problem. The same subtraction can be harmless at one magnitude and poorly conditioned at another.


Finding Machine Epsilon

Why this matters: machine epsilon is the local scale at which a floating-point number near 1 stops changing when you perturb it.

NumPy defines machine epsilon as the distance from 1 to the next larger representable number:

ϵmachine=nextafter⁡(1,+∞)−1.\epsilon_{\mathrm{machine}}=\operatorname{nextafter}(1,+\infty)-1.

Under round-to-nearest, a perturbation just larger than half this spacing can already change the rounded sum. Therefore, “the smallest positive number that changes 1 when added” is not the same definition. The halving experiment below searches powers of two and recovers the spacing, not every possible perturbation. NumPy’s definition.

For IEEE 754 double precision, the value is about

ϵmachine≈2.22×10−16.\epsilon_{\mathrm{machine}} \approx 2.22\times 10^{-16}.

This means that near numbers of order unity, relative perturbations much smaller than 10-16 are typically invisible in double precision.

Worked Example 1: Why 0.1 + 0.2 != 0.3

Why this matters: this is the simplest way to see that floating-point numbers are binary approximations, not exact decimal objects.

What quantity is being computed?

What is known exactly?

What is represented approximately on the computer?

What does the error mean in practice?

value = 0.1                  stored as 3602879701896397/36028797018963968 = 0.10000000000000001
value = 0.2                  stored as 3602879701896397/18014398509481984 = 0.20000000000000001
value = 0.3                  stored as 5404319552844595/18014398509481984 = 0.29999999999999999
value = 0.30000000000000004  stored as 1351079888211149/4503599627370496 = 0.30000000000000004
0.1 + 0.2 == 0.3 -> False
0.1 + 0.2        -> 0.30000000000000004
0.3              -> 0.29999999999999999

Interpretation: the mismatch is not folklore and it is not a Python bug. The finite binary approximation of 0.1 is slightly different from the exact real number 0.1, and those tiny differences survive arithmetic.

Signed representation errors are nonzero for 0.1, 0.2, and 0.3; the binary fraction 0.5 is exact.

Figure 3:What to notice in this figure: the issue is not that the computer is careless. The issue is that most finite decimal fractions do not have finite binary expansions, so the stored values are the nearest available binary fractions.

Open full-size figure

Worked Example 2: Computing ϵmachine\epsilon_{\mathrm{machine}}

Why this matters: the abstract definition of machine epsilon becomes much easier to trust once you compute it yourself and compare it to NumPy’s built-in value.

What quantity is being computed?

What is known exactly?

What is represented approximately on the computer?

What does the error mean in practice?

Loop result:   2.220446049250313081e-16
NumPy result:  2.220446049250313081e-16
Relative gap:  0.000e+00

Interpretation: the loop and the library agree because both are describing the same hardware-level double-precision format. The result is approximately 2.22×10−162.22\times 10^{-16}, not exactly 10-16, because binary floating-point spacing is not organized in decimal powers.

Machine epsilon is a local spacing scale, not a universal error budget.


The Three Types of Error

Why this matters: students often treat all numerical error as one fuzzy category, but different errors come from different causes and require different fixes.

Representation Error: Round-off Error

Why this matters: if the computer cannot represent a number exactly, error is present before your algorithm even begins.

Student mental model: round-off error is a storage problem. The value you wanted and the value the computer stores are nearby, but not identical.

We can write the local scale of round-off influence schematically as

Eround∝ϵmachine.E_{\mathrm{round}} \propto \epsilon_{\mathrm{machine}}.

This equation says that representation error is tied to floating-point spacing, though the proportionality constant depends strongly on the problem and the operation being performed.

Algorithmic Approximation Error: Truncation Error

Why this matters: even with exact arithmetic, many scientific algorithms intentionally approximate an infinite process with a finite one.

Student mental model: truncation error is an algorithm-design problem. You stop a series, discretize a derivative, or take a finite timestep, and that choice introduces approximation error.

For a method of order pp, a standard local model is

Etrunc∝hp.E_{\mathrm{trunc}} \propto h^p.

This equation says that the error decreases as the resolution parameter hh shrinks, provided you are in the regime where the asymptotic model actually applies.

Accumulation and Amplification Error

Why this matters: a tiny per-step error can still become scientifically important if the computation repeats the step many times or amplifies perturbations dynamically.

Student mental model: propagation error is a history problem. The error you make now can be carried, accumulated, or magnified by everything that happens later.

If the signs of individual errors behave roughly like independent random kicks, a common model is

Eacc∼n ϵmachine.E_{\mathrm{acc}} \sim \sqrt{n}\,\epsilon_{\mathrm{machine}}.

This equation says that random-sign accumulation behaves like a random walk, so the total error grows more slowly than linearly with the number of steps nn.

If the bias is coherent from step to step, a conservative model is

Eacc∼n ϵmachine.E_{\mathrm{acc}} \sim n\,\epsilon_{\mathrm{machine}}.

This equation says that systematic bias can accumulate linearly with the number of steps, which is much more dangerous for long integrations.

The takeaway is that different error sources call for different fixes. Better algebra, better resolution, and better diagnostics solve different problems.


Catastrophic Cancellation

Why this matters: catastrophic cancellation is the signature failure mode of subtracting nearly equal numbers in floating-point arithmetic.

Student mental model: when two nearly equal numbers are subtracted, the leading digits cancel and the remaining digits may contain very little reliable information. The problem is not the subtraction itself; the problem is that the subtraction exposes the least accurate part of both inputs.

Worked Example 3: A Stable and Unstable Parallax-Like Difference

Why this matters: astronomy often asks us to compare nearby distances, inverse distances, or optical-depth-like quantities where the naive algebra is numerically brittle.

What quantity is being computed?

What is known exactly?

What is represented approximately on the computer?

What does the error mean in practice?

Naive value:      6.705747068735945504e-14 AU^-1
Stable value:     6.705747068735496169e-14 AU^-1
Relative gap:     6.701e-14

Interpretation: the reformulation subtracts the stored distances directly, before rounding two reciprocal evaluations. Nearby floating-point distances can subtract exactly even though they approximate the intended decimal inputs. To see what the algebra repairs, we need two references: the intended decimal problem and the problem defined by the stored binary inputs.

Input representation contribution: 5.747E-17 AU^-1
Naive evaluation error:            4.497E-27 AU^-1
Reformulated evaluation error:     3.353E-30 AU^-1
Total error against decimal data (naive): 5.747E-17
Total error against decimal data (reformulated): 5.747E-17

The reformulated expression evaluates the stored-input problem more accurately. The total errors against the intended decimals remain nearly equal because the input representation contribution dominates. Algebra cannot recover information already lost when the distances were stored. This distinction matters when a code improvement appears to have no effect on the final scientific observable.

Stable Reformulation

Why this matters: when a numerical expression is fragile, the first fix is often algebraic rather than algorithmic.

Another classic example is

a2−b2a−b=a+b.\frac{a^2-b^2}{a-b} = a+b.

This equation says that if aa and bb are close, the left-hand side performs two unstable operations before dividing, while the right-hand side avoids the cancellation entirely.

Reciprocal-difference errors against intended decimal inputs and stored binary64 inputs. Rewriting reduces evaluation error, while input representation error remains.

Figure 4:The intended dimensionless inputs are d1=1d_1=1 and d2=1+δd_2=1+\delta. Both formulas use the same stored binary64 inputs. The left panel compares their answers with a 90-digit decimal reference for the intended inputs; the right uses a 90-digit reference for the stored inputs. Rewriting the reciprocal difference reduces evaluation error to about floating-point precision, but it cannot recover the input digits already lost when storing 1+δ1+\delta. Consequently, the total errors can remain comparable. No artificial error floor is plotted.

Open full-size figure

Algebraic equivalence does not imply numerical equivalence. Catastrophic cancellation is a conditioning problem exposed by subtraction.


Measuring Errors: Absolute vs. Relative

Why this matters: the same numerical difference can be negligible in one context and catastrophic in another.

We define absolute error by

Eabs=∣xcomputed−xtrue∣.E_{\mathrm{abs}} = \lvert x_{\mathrm{computed}} - x_{\mathrm{true}} \rvert.

This equation measures the raw size of the discrepancy in the physical units of the quantity itself.

We define relative error by

Erel=∣xcomputed−xtrue∣∣xtrue∣=Eabs∣xtrue∣.E_{\mathrm{rel}} = \frac{\lvert x_{\mathrm{computed}} - x_{\mathrm{true}} \rvert} {\lvert x_{\mathrm{true}} \rvert} = \frac{E_{\mathrm{abs}}}{\lvert x_{\mathrm{true}} \rvert}.

This equation measures the discrepancy relative to the size of the true value, which is often more meaningful away from zero.

The threshold in this helper is an illustrative reporting heuristic, not a definition of zero or an acceptance tolerance. It does not suppress a defined relative error: for a nonzero reference of 10-8 and a computed value of 2×10−82\times10^{-8}, the relative error is 1, or 100%. Whether that matters requires a physical accuracy target.

Worked Example 4: Computing Jupiter’s Mass

Why this matters: a large absolute difference can still be an excellent result if the reference quantity is enormous.

What quantity is being computed?

What is known exactly?

What is represented approximately on the computer?

What does the error mean in practice?

The absolute error is

Eabs=∣1.897×1030 g−1.898×1030 g∣=1.0×1027 g.E_{\mathrm{abs}} = \left\lvert 1.897\times 10^{30}\,\mathrm{g} - 1.898\times 10^{30}\,\mathrm{g} \right\rvert = 1.0\times 10^{27}\,\mathrm{g}.

This equation says the raw discrepancy is one thousand trillion trillion grams, which sounds enormous until we compare it with Jupiter’s full mass.

The relative error is

Erel=1.0×1027 g1.898×1030 g≈5.27×10−4≈0.0527%.E_{\mathrm{rel}} = \frac{1.0\times 10^{27}\,\mathrm{g}} {1.898\times 10^{30}\,\mathrm{g}} \approx 5.27\times 10^{-4} \approx 0.0527\%.

This equation says the computation is wrong by only about five-hundredths of a percent, which is excellent for many astrophysical purposes.

Absolute error: 1.000e+27 g
Relative error: 5.269e-04
Recommended metric: relative

Interpretation: the absolute error is physically large because Jupiter is physically large. The relative error is the better summary here because the true value is nowhere near zero.

Worked Example 5: Near-Zero Failure of Relative Error

Why this matters: near zero, relative error can look dramatic even when the physical discrepancy is negligible.

What quantity is being computed?

What is known exactly?

What is represented approximately on the computer?

What does the error mean in practice?

Absolute error:      1.000e-08 cm s^-1
Relative error:      1.000e+00
Recommended metric:  absolute

Interpretation: the relative error sounds catastrophic because the denominator is tiny. In practice, the absolute error may be the more meaningful diagnostic when the physically relevant baseline is close to zero.

<Figure size 850x580 with 2 Axes>

What to notice: the top panel shows three separate numerical examples in their stated units. A mass error in grams cannot be ranked physically against a velocity error in centimeters per second by comparing these numbers. The bottom panel uses dimensionless relative errors, which allow a fractional comparison across scales. The near-zero reference still needs care: a large fractional error can correspond to a small absolute discrepancy. Whether that discrepancy matters depends on the physical accuracy required.

Relative error is powerful away from zero and often misleading near zero.


From measured errors to atol and rtol

An error measures a discrepancy. A tolerance states how much discrepancy a particular comparison permits. Increasing the tolerance changes the decision, not the computed result. We should choose it from a physical target, a method error bound, or a justified numerical comparison before seeing whether a test passes.

For finite values, NumPy’s isclose(computed, reference, rtol=r, atol=a) tests

∣computed−reference∣≤a+r∣reference∣.|\mathrm{computed}-\mathrm{reference}| \le a+r|\mathrm{reference}|.

The reference belongs second: NumPy uses it for the relative scale. atol has the quantity’s units, while rtol is dimensionless (0.01 means 1%). The allowances add; passing does not mean satisfying both limits separately. At zero reference only atol contributes. NumPy comparison semantics.

Predict: the following residuals are in centimeters. The example’s stated criterion allows 10-8 cm plus one part in a million of the reference. Which rows pass? Write the allowed error before calling np.isclose.

absolute errors [cm]: [3.e-09 1.e-01 1.e-01]
allowed errors [cm]:  [1.00000000e-08 1.01000000e-06 1.00000001e+00]
accepted: [ True False  True]
zero, relative only: False
tiny reference, defaults: True
tiny reference, 1%: False

The first and third rows pass, for different reasons. At zero, the absolute allowance accepts the residual; at 106 cm the relative allowance is about 1 cm. The same 0.1 cm discrepancy fails at 1 cm. The tiny-reference example shows why defaults deserve scrutiny: a 100% relative error passes when the default absolute allowance is much larger than either value.

A shaded band shows accepted computed-minus-reference residuals; its width is constant near zero and increases with reference magnitude.

The boundary is ±(atol+rtol∣reference∣)\pm(\mathrm{atol}+\mathrm{rtol}|\mathrm{reference}|). The plotted example uses atol=0.1 cm and rtol=0.1 so both contributions are visible. They are illustrative scales, not recommended course-wide tolerances.

Open full-size figure

np.isclose works element by element; np.allclose requires all comparisons to pass. Check array shapes so broadcasting does not silently compare the wrong objects. Also check finiteness when your mathematical model requires finite values: a closeness test is not a complete input or result validation.

Lecture 1’s math.isclose uses a different rule for finite scalars: max⁡(abs_tol,rel_tolmax⁡(∣a∣,∣b∣))\max(\mathrm{abs\_tol},\mathrm{rel\_tol}\max(|a|,|b|)). It is symmetric and takes a maximum rather than a sum. Read the API rather than assuming a rename preserves the criterion. In pytest.approx, the allowance is also a maximum of its supplied absolute and expected-value-scaled relative tolerances.

Tracking Accumulated Error

Why this matters: long-running simulations care not just about per-step error, but about what happens to those errors after thousands, millions, or billions of updates.

Worked Example 6: Error Propagation Over Many Steps

Why this matters: repeated updates can turn an individually tiny error into a meaningful drift.

What quantity is being computed?

What is known exactly?

What is represented approximately on the computer?

What does the error mean in practice?

steps =        1  random model = 2.220e-16  systematic model = 2.220e-16
steps =       10  random model = 7.022e-16  systematic model = 2.220e-15
steps =      100  random model = 2.220e-15  systematic model = 2.220e-14
steps =    10000  random model = 2.220e-14  systematic model = 2.220e-12
steps =  1000000  random model = 2.220e-13  systematic model = 2.220e-10

Interpretation: the random-walk model grows more slowly because positive and negative errors partially cancel. The systematic model grows faster because every step pushes in the same direction.

Independent zero-mean additive errors have root-N RMS growth, while equal same-sign errors grow as N. Five random-walk histories cross the RMS scale, which is not a bound.

Figure 6:These are additive models with the illustrative dimensionless scale σ=b=10−15\sigma=b=10^{-15}: independent zero-mean errors with standard deviation σ\sigma give RMS accumulated error σN\sigma\sqrt{N}, while identical positive errors bb give bNbN. The five seeded histories use independent steps ±σ\pm\sigma; the dashed RMS scale is not a bound on a history. The models contain no dynamical amplification and do not prescribe tolerances from simulation duration.

Open full-size figure

The takeaway is that repeated tiny errors matter because history matters. Growth law, sign structure, and amplification all change the scientific risk.


Adaptive Error Control

Why this matters: many algorithms cannot afford to run with a single fixed step size or tolerance everywhere.

Suppose an error estimator gives a current value EestimatedE_{\mathrm{estimated}}. A basic tolerance rule is

Eestimated>tol⇒reduce step size.E_{\mathrm{estimated}} > \mathrm{tol} \quad \Rightarrow \quad \text{reduce step size}.

This equation says that if the estimated error exceeds the target tolerance, the current resolution is not adequate.

For a method of order pp, a typical controller uses

hnew∝hcurrent(tolEestimated)1/(p+1).h_{\mathrm{new}} \propto h_{\mathrm{current}} \left(\frac{\mathrm{tol}}{E_{\mathrm{estimated}}}\right)^{1/(p+1)}.

This equation says that higher estimated error should trigger a smaller next step, with the exact exponent depending on the error model of the method.

The simplified helper below is only a heuristic controller, not a universal adaptive-step law.

In a real solver, the controller exponent and update logic depend on the method and the error estimator, so this helper is illustrative rather than canonical.

estimated error = 1.0e-02  h_new = 1.000e-02
estimated error = 5.0e-05  h_new = 1.000e-01
estimated error = 0.0e+00  h_new = 2.000e-01

Interpretation: a large error estimate reduces the step size, a very small one allows a larger step, and the zero-error case is treated cautiously rather than dividing by zero.

A feedback diagram compares coarse and fine estimates, accepts a step within its allowance, or refines and retries.

Figure 7:Adaptive control is a feedback loop. The algorithm changes its step size in response to an error estimate, trading computational work against a stated accuracy target.

Open full-size figure

The takeaway is that adaptive control is a method-specific feedback design, not a single rule to memorize.


Error Tolerances in Practice

Why this matters: students often want a single “correct tolerance,” but useful targets depend on the quantity being monitored, the scale of the physics, and the scientific question.

The table below gives context-dependent examples, not universal commandments. It is also not a set of values to memorize. It is meant to help you ask better questions about scale, diagnostics, and scientific purpose.

ApplicationTypical Numerical TargetWhy That Target Is ChosenCautionary Note
Quadrature benchmarkA tolerance justified by the rule’s observed order and the exact referenceThe sine integral and π4/15\pi^4/15 make a measured error possibleA test tolerance must be looser than the expected numerical error but tight enough to catch a broken rule
Long conservative integrationProblem-dependent conservation monitor plus convergence studyLong runs accumulate error, so invariants and cross-resolution checks matter togetherA single scalar tolerance is rarely enough
Stellar structure or evolution solveResiduals around 10-6 to 10-8, depending on model and solverOften below dominant input-physics uncertainty while still numerically stableDo not confuse solver tolerance with physical uncertainty
Galaxy or cluster simulationGlobal conservation and ensemble statistics at about 10-4 to 10-6, depending on the observableOften the global structure matters more than trajectory-level perfectionParticle-level exactness is usually not the goal
MCMC samplingDiagnostics such as R^<1.01\hat{R} < 1.01 and adequate effective sample sizeSampling quality is assessed statistically rather than by one floating-point toleranceDetailed balance is a property of the algorithm, not a “tolerance row”
Gradient-based optimizationRelative loss or gradient norm threshold around 10-6 or a problem-scaled stopping ruleUseful when further iterations do not change the model meaningfullyStop criteria should reflect model scale and noise, not just machine precision

Interpretation: numerical targets are chosen to answer a scientific question reliably, not to win a contest for the smallest printed number.


Convergence Testing Without True Values

Why this matters: in research code, you often do not know the true answer. You need a way to test whether refinement is behaving consistently with the order of the method.

This section combines three related but different ideas:

Suppose the leading error behaves like

E(h)≈Chp,E(h) \approx C h^p,

where pp is the order of the method.

This equation says that halving hh should reduce the leading error by about a factor of 2p2^p once the solution is in the asymptotic regime.

If you compute the same quantity at two resolutions, hh and h/2h/2, then a Richardson-style estimate is

Eestimated≈∣fh−fh/2∣2p−1.E_{\mathrm{estimated}} \approx \frac{\lvert f_h - f_{h/2} \rvert}{2^p - 1}.

This equation says that the difference between two resolutions can estimate the error of the finer one when the asymptotic model is valid.

h = 0.10000  approximation = 0.539402252170
h = 0.05000  approximation = 0.540077208046
h = 0.02500  approximation = 0.540246026137
h = 0.01250  approximation = 0.540288235606

Successive-difference ratios:
ratio between h = 0.10000 and h = 0.05000: 3.998
ratio between h = 0.05000 and h = 0.02500: 4.000

Richardson-style error estimates for the finer solution:
fine h = 0.05000  E_estimated = 2.250e-04
fine h = 0.02500  E_estimated = 5.627e-05
fine h = 0.01250  E_estimated = 1.407e-05

Interpretation: for a second-order method, the successive-difference ratios should trend toward 22=42^2 = 4 in the asymptotic regime. If they do not, you should check whether round-off, non-smoothness, or implementation bugs are interfering.

Convergence tests check whether the method behaves like the theory predicts, not whether one number merely looks plausible.


Bridge to Part 3

Why this matters: this lesson handled the arithmetic side of numerical error, but not yet the full approximation model of a discretized method.

Part 2 gave us a language for representation error, cancellation, error metrics, accumulation, and tolerance logic. Part 3 adds the Taylor-series viewpoint that models truncation error directly and shows why different finite-difference formulas have different orders.

The key connection is this:

Part 3 unifies these ideas by showing how a real computation lives at the intersection of approximation error and floating-point error.

The takeaway to carry forward is that numerical trust comes from both arithmetic awareness and approximation theory.

Next: Part 3 - Taylor Series