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.

A Motivating Example

Consider computing f(x)=x+1xf(x) = \sqrt{x+1} - \sqrt{x} for large xx.

The condition number is κ1/2\kappa \leq 1/2 — this is a well-conditioned problem!

Yet using 4-digit decimal arithmetic:

f(1984)=19851984=44.5544.54=0.01f(1984) = \sqrt{1985} - \sqrt{1984} = 44.55 - 44.54 = 0.01

The true value is 0.01122. We lost two significant digits — 10% relative error from a well-conditioned problem!

What went wrong? The algorithm subtracts two nearly-equal numbers, amplifying their individual roundoff errors. This is called subtractive cancellation.

A stable alternative: Rationalize the expression:

x+1x=(x+1x)(x+1+x)x+1+x=1x+1+x=189.09=0.01122\sqrt{x+1} - \sqrt{x} = \frac{(\sqrt{x+1} - \sqrt{x})(\sqrt{x+1} + \sqrt{x})}{\sqrt{x+1} + \sqrt{x}} = \frac{1}{\sqrt{x+1} + \sqrt{x}} = \frac{1}{89.09} = 0.01122 \quad \checkmark

Same problem, different algorithm, full accuracy.

This example reveals something fundamental: not all errors are the problem’s fault. We need a framework to separate algorithm quality from problem sensitivity.

Forward Error

Consider computing y=f(x)y = f(x). Due to roundoff or approximation, we actually compute y~\tilde{y}.

Forward error is the most intuitive measure—it directly answers “how wrong is my answer?”

Absolute vs. Relative Error

For meaningful comparisons, we often use relative error:

Example: An error of 0.001 means different things for:

Relative error captures this distinction. When we say “accurate to 6 digits,” we mean relative error 106\approx 10^{-6}.

The Catch

Forward error seems like the natural measure—we want to know how wrong we are! But there’s a problem:

Forward error requires knowing the true answer y=f(x)y = f(x).

If we knew the true answer, we wouldn’t need to compute it. This is where backward error comes in.

Backward Error

Instead of asking “how wrong is my answer?”, backward error asks a different question.

In other words: our computed y~\tilde{y} might not equal f(x)f(x), but it does equal f(x~)f(\tilde{x}) for some nearby x~\tilde{x}. The backward error measures how far x~\tilde{x} is from xx.

Why Backward Error?

At first, backward error seems less useful than forward error. But it turns out to be more fundamental for algorithm analysis. Here’s why:

1. Backward error is computable

Forward error requires knowing the true answer—but if we knew that, we wouldn’t need to compute it!

Backward error, on the other hand, is often directly computable:

2. Backward error separates algorithm from problem

There are two sources of error:

Backward error isolates the first question. If an algorithm has small backward error, it’s doing its job well—any remaining forward error is the problem’s fault (ill-conditioning), not the algorithm’s.

3. Backward error accounts for input uncertainty

Real-world inputs have measurement error. If your input xx has uncertainty δ\delta, then:

Principle: Don’t ask for more accuracy than your inputs justify.

Residual as Backward Error

For many problems, backward error equals the residual—a directly computable quantity.

ProblemResidualInterpretation
Linear system Ax=bAx = br=bAx~r = b - A\tilde{x}x~\tilde{x} solves Ax=brAx = b - r exactly
Root finding f(x)=0f(x) = 0f(r~)f(\tilde{r})r~\tilde{r} is a root of f(x)f(r~)f(x) - f(\tilde{r})
Least squaresbAx~|b - A\tilde{x}|How well x~\tilde{x} fits the data

Key insight: Backward stability implies forward stability (for well-conditioned problems). This is why we focus on backward stability—it’s the stronger, more useful guarantee.

The Relationship: Forward Error ≤ κ × Backward Error

Now we connect backward error to forward error using the condition number from the previous section.

Derivation

Suppose we compute y~\tilde{y} instead of the true y=f(x)y = f(x). If the algorithm is backward stable:

y~=f(x~)for some x~ near x\tilde{y} = f(\tilde{x}) \quad \text{for some } \tilde{x} \text{ near } x

The forward error is the difference y~y=f(x~)f(x)|\tilde{y} - y| = |f(\tilde{x}) - f(x)|.

By Taylor expansion:

f(x~)f(x)f(x)x~x|f(\tilde{x}) - f(x)| \approx |f'(x)| \cdot |\tilde{x} - x|

Converting to relative errors:

y~yyf(x)f(x)x~x=xf(x)f(x)κx~xx\frac{|\tilde{y} - y|}{|y|} \approx \frac{|f'(x)|}{|f(x)|} \cdot |\tilde{x} - x| = \underbrace{\left|\frac{x f'(x)}{f(x)}\right|}_{\kappa} \cdot \frac{|\tilde{x} - x|}{|x|}

What This Means

ComponentControlled byCan we improve it?
Forward errorBothIndirectly
Condition number κ\kappaThe problemNo (it’s math)
Backward errorThe algorithmYes!

Practical implications:

Unstable Algorithms: Common Pitfalls

Even well-conditioned problems can produce terrible results with unstable algorithms. Three operations are particularly dangerous:

OperationProblemStable Alternative
Subtracting close numbersRelative error explodesReformulate algebraically
Dividing by small numbersAmplifies numerator errorUse logarithms or rescale
Adding numbers of vastly different magnitudeSmall values lostSort and sum smallest first
Example: The Quadratic Formula

Solve x2106x+1=0x^2 - 10^6 x + 1 = 0. The roots are x1106x_1 \approx 10^6 and x2106x_2 \approx 10^{-6}.

Standard formula for the small root:

x2=1061012421061062=0x_2 = \frac{10^6 - \sqrt{10^{12} - 4}}{2} \approx \frac{10^6 - 10^6}{2} = 0

Catastrophic cancellation! The true value is 10-6, so we have 100% relative error.

Stable alternative: Use x1x2=c/a=1x_1 x_2 = c/a = 1, so x2=1/x1x_2 = 1/x_1. Compute x1x_1 (no cancellation), then x2=1/x1x_2 = 1/x_1 with full precision.

Example: Computing ex1e^x - 1

For small xx, direct computation fails:

x = 1e-15
result = np.exp(x) - 1  # Returns ~1.1e-15 (only 1 correct digit!)

Why? e10151.000000000000001e^{10^{-15}} \approx 1.000000000000001, stored as 1.0 due to limited precision. Then 1.01.001.0 - 1.0 \approx 0.

Stable alternative: Use the dedicated function:

result = np.expm1(1e-15)  # Returns 1.0e-15 (full precision!)
Example: Kahan Summation

When summing many numbers, roundoff errors accumulate. Kahan summation tracks and compensates for these errors:

def kahan_sum(x):
    """Compensated summation — much more accurate than naive sum."""
    s = 0.0  # Running sum
    c = 0.0  # Compensation for lost low-order bits
    for xi in x:
        y = xi - c        # Compensated value to add
        t = s + y         # New sum (may lose precision)
        c = (t - s) - y   # Recover what was lost
        s = t
    return s

This achieves O(εmach)O(\varepsilon_{\text{mach}}) error instead of O(nεmach)O(n \cdot \varepsilon_{\text{mach}}) for naive summation.

Summary

ConceptMeasuresControlled byComputable?
Forward errorDistance to true answerProblem + algorithmUsually not
Backward errorPerturbation to inputAlgorithmOften yes (residual)
Condition numberSensitivity of problemProblem (math)Sometimes

The workflow:

  1. Check if the problem is well-conditioned (small κ\kappa)

  2. Use a backward-stable algorithm (small backward error)

  3. Then forward error will be small automatically

If forward error is large despite backward stability, the problem is ill-conditioned—reformulate if possible.