Conditioning and Numerical Stability

Published on 2026-11-04·v1.0

Learning Objectives

  • Define the relative condition number of a problem, compute it with its hypotheses, and say where it blows up.
  • Distinguish conditioning, a property of the problem, from stability, a property of the algorithm.
  • Compute the forward error and the backward error of a computed answer.
  • Use the rule that forward error is at most about condition number times backward error, and state what it does and does not promise.
  • Diagnose an unstable formula and replace it with a stable one, or recognize that no replacement can help.

Context & Motivation

Example 2 of sources-of-error-truncation-rounding-propagation lost every digit of 1−cos⁡x1 - \cos x, and a rewrite recovered them all. That rewrite will not always exist. Two failures look identical on screen and call for opposite responses. Sometimes the answer is so sensitive to its input that rounding the input alone destroys it, and only better data or a different question can help. Sometimes the problem is perfectly healthy and the formula is what wastes the digits.

Telling these apart is this lesson's subject, and it decides what to do next: rewrite the code, raise the precision, or question the input. The same vocabulary carries through the discipline. bisection-and-convergence-rate reads a small residual as a backward error, and the-matrix-condition-number extends the condition number to Ax=bAx = b.

Core Theory

Conditioning: how sensitive is the problem

Treat a problem as a map ff from data to answer. Conditioning asks how much the exact answer moves when the data moves, with no algorithm involved. If x≠0x \ne 0, f(x)≠0f(x) \ne 0 and ff is differentiable at xx, the relative condition number is

κf(x)=∣xf′(x)f(x)∣,\kappa_f(x) = \left\lvert\frac{x f'(x)}{f(x)}\right\rvert,

the limit, as the perturbation shrinks, of relative change in ff over relative change in xx. So a relative perturbation η\eta in the input moves the answer by about κf(x)∣η∣\kappa_f(x)\lvert\eta\rvert. Stored inputs carry relative error up to uu, so even a perfect algorithm should expect a relative error near κu\kappa u in float64, a loss of about log⁡10κ\log_{10}\kappa digits. This is a first-order statement, and a problem with κ\kappa near 11 is well-conditioned while one with large κ\kappa is ill-conditioned.

Problem κf(x)\kappa_f(x) Large when Formula at sample point Measured
x\sqrt{x} 1/21/2 never 0.50.5 at x=2x=2 0.50.5
exe^x ∣x∣\lvert x\rvert ∣x∣\lvert x\rvert large 2020 at x=20x=20 2020
ln⁡x\ln x 1/∣ln⁡x∣1/\lvert\ln x\rvert xx near 11 10000.510000.5 at x=1.0001x=1.0001 10000.510000.5
sin⁡x\sin x ∣xcot⁡x∣\lvert x\cot x\rvert xx near nπn\pi, n≠0n\ne0; most large ∣x∣\lvert x\rvert 1971.61971.6 at x=3.14x=3.14 1971.61971.6
x−1x - 1 ∣x∣/∣x−1∣\lvert x\rvert/\lvert x-1\rvert xx near 11 1000110001 at x=1.0001x=1.0001 1000110001

The measured column perturbs xx by a relative 10−2010^{-20} in high-precision arithmetic and divides the two relative changes. For sin⁡\sin, size alone also hurts: κ=2.68×106\kappa = 2.68\times10^6 at x=106x = 10^6, unless xx is near π/2+nπ\pi/2 + n\pi, where cot⁡x\cot x vanishes. The last row is the subtraction x−yx - y with y=1y = 1 held exact, so only xx carries error. The factor (∣x∣+∣y∣)/∣x−y∣(\lvert x\rvert + \lvert y\rvert)/\lvert x - y\rvert from the previous lesson assumes both operands do, and gives 2000120001 here.

The hypotheses matter. At a zero of ff the relative number does not exist, and the absolute version ∣f′(x)∣\lvert f'(x)\rvert takes over. math.sin(math.pi) returns 1.2246×10−161.2246\times10^{-16}, which is not a defect of sin: the stored math.pi differs from π\pi by that same amount, and sin⁡\sin has slope −1-1 there.

Conditioning also depends on what counts as data. As a function of xx, ln⁡x\ln x is ill-conditioned at x=1+δx = 1 + \delta, because rounding xx has already damaged δ\delta. As a function of δ\delta, ln⁡(1+δ)\ln(1+\delta) has κ\kappa near 11. That is why math.log1p(delta) exists: when δ\delta is known exactly, pass δ\delta.

Stability: how good is the algorithm

An algorithm computes f^(x)\hat f(x). Its forward error is the distance to the true answer, ∣f^(x)−f(x)∣\lvert\hat f(x) - f(x)\rvert, usually taken relative to ∣f(x)∣\lvert f(x)\rvert. Its backward error asks a different question: which problem did the algorithm solve exactly? It is the smallest relative perturbation ∣Δx∣/∣x∣\lvert\Delta x\rvert/\lvert x\rvert with f^(x)=f(x+Δx)\hat f(x) = f(x + \Delta x).

An algorithm is backward stable if its backward error is a modest multiple of uu for every input. Loosely, an algorithm is stable if its forward error is not much larger than κu\kappa u, the amount conditioning already forces. An unstable algorithm loses more than the problem requires.

Connecting the two

If f^(x)=f(x+Δx)\hat f(x) = f(x + \Delta x), the definition of κ\kappa gives, to first order,

∣f^(x)−f(x)∣∣f(x)∣≲κf(x) ∣Δx∣∣x∣.\frac{\lvert\hat f(x) - f(x)\rvert}{\lvert f(x)\rvert} \lesssim \kappa_f(x)\,\frac{\lvert\Delta x\rvert}{\lvert x\rvert}.

This is an upper bound and it reads as a diagnostic:

  • Backward error near uu and κ\kappa moderate: the answer is accurate.
  • Backward error near uu and κ\kappa huge: the answer may be poor, and the algorithm is not to blame.
  • Forward error far above κu\kappa u: the algorithm is to blame.

How formulas go unstable

An algorithm is a chain of steps, and each step is a small problem with its own condition number. A common failure route is a well-conditioned whole that passes through an ill-conditioned step: errors from earlier steps are then magnified, and nothing later can recover them. The lost digits of 1−cos⁡x1 - \cos x fit this pattern. The whole map has κ=∣xsin⁡x/(1−cos⁡x)∣\kappa = \lvert x\sin x/(1 - \cos x)\rvert, which tends to 22 as x→0x \to 0, so the problem is well-conditioned. The bad step is the last one: y↦1−yy \mapsto 1 - y at y=cos⁡xy = \cos x has κ=∣y∣/∣1−y∣≈2/x2\kappa = \lvert y\rvert/\lvert 1 - y\rvert \approx 2/x^2, which is 2×10162\times10^{16} at x=10−8x = 10^{-8}. The rewrite 2sin⁡2(x/2)2\sin^2(x/2) has no such step. Both unstable examples below fail this way.

Worked Examples

Example 1: a healthy problem, a bad formula

Solve x2+bx+c=0x^2 + bx + c = 0 with b=−108b = -10^8 and c=1c = 1. The roots are r1≈108r_1 \approx 10^8 and r2≈10−8r_2 \approx 10^{-8}. Differentiating r2+br+c=0r^2 + br + c = 0 gives the condition numbers of a root rr with respect to each coefficient: κb=∣b/(2r+b)∣\kappa_b = \lvert b/(2r+b)\rvert and κc=∣c/(r(2r+b))∣\kappa_c = \lvert c/(r(2r+b))\rvert. For r2r_2 both equal 11 to many digits. The problem is well-conditioned.

The textbook formula r2=(−b−b2−4c)/2r_2 = (-b - \sqrt{b^2-4c})/2 subtracts 99999999.9999999999999999.99999999 from 10810^8. That one step has condition number about 101610^{16}. The stable alternative computes r1r_1 with the signs that add, then uses r1r2=cr_1 r_2 = c:

text
formula r2 forward error backward error in c (-b - sqrt(D))/2 7.450580596923828e-09 2.5e-01 2.5e-01 c / r1 1e-08 7.9e-17 7.9e-17

The backward error comes from c′=−r^2−br^c' = -\hat r^2 - b\hat r, the coefficient for which r^\hat r is an exact root. The bad formula solved an equation with cc wrong by 25 percent, and with κ≈1\kappa \approx 1 the forward error matches, as the bound predicts. numpy.roots([1, -1e8, 1]) is also fine here, with relative error 8.6×10−178.6\times10^{-17} on this root.

The rewrite assumes r1≠0r_1 \ne 0 and helps with cancellation between bb and the square root. If b2≈4cb^2 \approx 4c the roots nearly coincide, the problem itself is ill-conditioned, and no rewrite restores the lost digits.

Example 2: the same numbers, computed in two directions

Let In=∫01xnx+10 dxI_n = \int_0^1 \frac{x^n}{x+10}\,dx. Since xn+10xn−1x+10=xn−1\frac{x^n + 10x^{n-1}}{x+10} = x^{n-1}, we get In=1n−10In−1I_n = \frac1n - 10 I_{n-1} for n≥1n \ge 1, with I0=ln⁡1.1I_0 = \ln 1.1. From 10≤x+10≤1110 \le x + 10 \le 11 on [0,1][0,1] also 111(n+1)≤In≤110(n+1)\frac{1}{11(n+1)} \le I_n \le \frac{1}{10(n+1)}. The forward recurrence in float64:

text
n forward I_n exact I_n error ratio 0 0.09531017980432493 0.09531017980432487 7.48e-17 1 0.04689820195675065 0.0468982019567514 7.48e-16 10.00 5 0.015352900839845474 0.015352900847328939 7.48e-12 10.00 10 0.008328713865253717 0.008327965518886312 7.48e-07 10.00 14 0.013578878977397152 0.006095415303348168 7.48e-03 10.00 16 0.7537212310730486 0.005374863668150086 7.48e-01 10.00 18 74.83944336874276 0.004806628252917123 7.48e+01 10.00

Every step multiplies the error by −10-10, as the recurrence says, so an ordinary rounding error of 7.5×10−177.5\times10^{-17} in I0I_0 reaches 7575 by n=18n = 18. At n=14n = 14 the value is already above the upper bound 1/1501/150.

Run it backward, In−1=110(1n−In)I_{n-1} = \frac{1}{10}\left(\frac1n - I_n\right), and each step divides the error by 1010. Starting from the crude guess I40=0I_{40} = 0 (true value 0.002220.00222), the error is 2.2×10−132.2\times10^{-13} at n=30n = 30 and 7.0×10−197.0\times10^{-19} at n=22n = 22, where the result 0.0039676510668801720.003967651066880172 differs from scipy.integrate.quad only in the last digit. The same numbers, reached two ways: one unstable, one stable. So the trouble was never the numbers. The forward route passes through the subproblem I0↦InI_0 \mapsto I_n, which amplifies absolute error by 10n10^n.

Example 3: a good algorithm, a terrible problem

Take the degree-20 Wilkinson polynomial p(x)=∏k=120(x−k)p(x) = \prod_{k=1}^{20}(x-k), with coefficients aia_i. Perturbing each aia_i by a relative ηi\eta_i moves the root kk by about −∑iaiηiki/p′(k)-\sum_i a_i\eta_i k^i/p'(k), so its condition number with respect to the coefficients is

κk=∑i∣ai∣kik ∣p′(k)∣.\kappa_k = \frac{\sum_i \lvert a_i\rvert k^i}{k\,\lvert p'(k)\rvert}.

numpy.roots, which computes eigenvalues of the companion matrix, gives on the expanded coefficients:

text
k computed root relative forward error kappa_k 1 0.9999999999999833 1.7e-14 4.2e+02 5 5.000000877361879 1.8e-07 8.2e+08 10 9.990574635182616 9.4e-04 5.6e+12 14 13.935190089077999 4.6e-03 5.4e+13 20 19.999888806572073 5.6e-06 1.4e+11

Now expand ∏i(x−r^i)\prod_i(x - \hat r_i) in exact arithmetic. Its coefficients differ from the true ones by a relative 1.2×10−141.2\times10^{-14} at most. So the computed roots are the exact roots of a polynomial that is almost the right one: backward error is tiny, while forward error reaches 4.6×10−34.6\times10^{-3}. For k=14k = 14 the bound gives 5.4×1013×1.2×10−14≈0.655.4\times10^{13} \times 1.2\times10^{-14} \approx 0.65, which the actual error respects by a wide margin.

The data itself is already rounded: five of these integer coefficients do not fit exactly in a float64. Exact arithmetic confirms the sensitivity. Changing the coefficient of x19x^{19} from −210-210 to −210−2−23-210 - 2^{-23}, a relative change of 5.7×10−105.7\times10^{-10}, sends roots to 16.73±2.81i16.73 \pm 2.81i. No algorithm that accepts rounded coefficients can undo that. The cure is a different representation: a program that holds the factors (x−k)(x-k) has no problem to solve.

Common Misconceptions & Pitfalls

  • "Ill-conditioned means the algorithm is bad." Conditioning involves no algorithm. Example 3 has a tiny backward error and a large forward error.
  • "A small residual means an accurate answer." It means a small backward error. Accuracy needs κ\kappa too.
  • "An accurate answer proves backward stability." Take f(x)=1+xf(x) = 1 + x at x=10−20x = 10^{-20}. The computed 1.0 has forward error 10−2010^{-20}, yet it is the exact answer only for x=0x = 0, a relative backward error of 11.
  • "More precision fixes instability." In Example 2 each extra decimal digit buys one more step. The cure is the backward direction.
  • "A function has one condition number." κ\kappa depends on the point and on what counts as data: ln⁡\ln has κ=10000.5\kappa = 10000.5 at 1.00011.0001 and 0.220.22 at 100100.
  • "The bound is an estimate." Example 3 shows 0.650.65 against an actual 4.6×10−34.6\times10^{-3}. It limits the worst perturbation, and the actual one need not be the worst.

Summary

The condition number κf(x)=∣xf′(x)/f(x)∣\kappa_f(x) = \lvert x f'(x)/f(x)\rvert measures how much the problem amplifies relative perturbations, before any algorithm runs. It needs x≠0x \ne 0, f(x)≠0f(x) \ne 0 and differentiability, and it depends on what is taken as data. Forward error compares the answer with the truth, and backward error asks which nearby problem was solved exactly. To first order, forward error is at most κ\kappa times backward error. A backward stable algorithm on a well-conditioned problem gives an accurate answer. On an ill-conditioned problem it gives a small backward error and possibly a poor answer, and a better algorithm cannot change that. When forward error far exceeds κu\kappa u, the formula is at fault, often through one ill-conditioned step, and rewriting it or running it in another direction can recover the digits.

Documentation Links