🎯Computer Arithmetic and Error
Floating-Point Representation
Binary fractions, IEEE 754, machine epsilon, rounding, overflow and underflow
Dhahran, 25 February 1991
Saudi Arabia, the last week of the Gulf War. A Patriot air-defence battery at Dhahran has been switched on for about 100 hours.
The Patriot predicts where an incoming missile will be next by multiplying its speed by elapsed time. Its computer counts time in tenths of a second, as an integer, and converts to seconds by multiplying by 0.1 held in a 24-bit register.
But 0.1 has no finite binary expansion. In binary it is 0.0001100110011001100… forever. The register keeps 23 bits after the point and throws the rest away. Each tick is short by about 0.000000095 seconds.
Nobody would notice that in an hour. After 100 hours — 3.6 million ticks — the clock is 0.34 seconds behind. A Scud travels well over half a kilometre in 0.34 s. When a Scud came in that evening, the radar looked for it where the drifted clock said it should be, found nothing in its range gate, and did not engage.
The missile hit a barracks. 28 American soldiers were killed.
The fault was known: a software fix that corrected the clock reached Dhahran the next day. This lesson is about what a computer actually stores when you write 0.1, and why the answer is never quite 0.1.
A real number can carry infinitely many digits. A computer word has 32 or 64 bits. Every number a program uses is therefore an approximation chosen from a finite set, and the whole of numerical analysis starts from one question: how far is the stored number from the one you meant, and what happens to that gap as the computation proceeds?
Integers are easy — every integer up to fits exactly in a double. Fractions are not. In base 10 the fraction 1/3 never terminates; in base 2 the same is true of every fraction whose denominator is not a power of two, including 1/10. So 0.1, 0.2 and 0.3 are all approximations in binary, and prints as .
Derivation
Why 0.1 never ends in binary
To write a fraction in base 2, multiply it by 2 again and again. The whole-number part that appears each time is the next binary digit; the fractional part is carried on to the next line.
The fractional part has come back, so from here the same four digits repeat for ever:
Why it must repeat: every fractional part that appears has the form with between 0 and 9, so after at most ten doublings one of them returns. The expansion stops only if a fractional part becomes exactly 0, which needs to be a whole number for some . Because , no power of 2 cancels the 5. The same argument shows that a fraction terminates in binary exactly when its denominator is a power of two.
Scientific notation fixes the precision, not the size: and both have four significant digits. Floating point is scientific notation in base 2. A number is stored as a sign, an exponent and a fixed number of significant bits, so the relative precision is the same whether the number is the mass of a galaxy or the charge of an electron.
IEEE 754 binary64 (double). s is one bit, the biased exponent e is 11 bits, the fraction f is 52 bits. Because a normalised binary number always starts with 1, that leading 1 is not stored — 53 significant bits for the price of 52.
| Format | Sign / exponent / fraction bits | Machine epsilon | Largest | Smallest normal | Decimal digits |
|---|---|---|---|---|---|
| binary32 (single) | 1 / 8 / 23 | about 7 | |||
| binary64 (double) | 1 / 11 / 52 | about 16 |
The two IEEE 754 formats every engineering code uses
💡Machine epsilon and unit roundoff
Machine epsilon is the gap between 1 and the next larger float: in double precision.
With round-to-nearest, any real in range is stored as with , where the unit roundoff is .
This one inequality is the model of every error analysis in this course: storing a number costs a relative error of at most . Not an absolute error — the gaps between floats grow with the numbers. Between 1 and 2 they are apart; between and they are 1 apart; above the odd integers are not representable at all.
Derivation
Why rounding costs at most
Take any real between two powers of two, . Every double in that range has the same exponent and differs only in its 52 fraction bits, so neighbouring doubles are a fixed distance apart: the value of the last bit,
Round-to-nearest moves to the closer of its two neighbours, so it moves it by at most half a gap:
Divide by , which is at least :
Call that relative error ; then with — the model above. Two consequences fall out of the same lines. The absolute gap doubles with every power of two, which is why it is 1 between and . And chopping can move by up to a whole gap, always towards zero, so its bound is , not .
📐What is stored for 0.1
Double precision rounds 0.1 to the nearest float, which is exactly
The error is , a relative error of — below , exactly as the model promises. In the binary pattern the exponent is (because ) and the fraction is the repeating pattern 1001 1001 … rounded up in its last bit.
The Patriot's register did two things worse: it kept only 23 fraction bits, and it chopped instead of rounding. Chopping 0.1 at 23 bits gives 0.09999990463… — an error of , about a billion times larger.
Derivation
Where the Patriot's s comes from
From the doubling table above, the binary digits of 0.1 are : digit is 1 when leaves remainder 2 or 3 on division by 4.
The register kept to and threw the rest away. The discarded tail starts at , and leaves remainder 2, so the tail reads :
A block of four digits repeated for ever is worth the block divided by (the binary version of in decimal). The block is 12, so . Therefore
The clock counted ticks correctly; each tick was converted to seconds with a number that was too small by this amount, always in the same direction.
The purpose of computing is insight, not numbers.
Rounding modes. IEEE 754 rounds to nearest by default, breaking ties to the value whose last bit is even. The alternatives — toward zero (chopping), toward , toward — exist for interval arithmetic and special uses. Round-to-nearest errs in both directions, so independent errors partly cancel. Chopping always errs the same way, so its errors add up linearly. That difference is the whole Patriot story:
.
The US General Accounting Office's report tabulated exactly this growth: 0.0275 s after 8 hours, 0.1648 s after 48 hours, 0.3433 s after 100 hours, by which point the range gate had shifted by 687 m.
Running out of range. The exponent has limits too.
- Overflow — a result larger than about becomes . Squaring overflows even though the answer you want, say , is modest;
math.hypotexists precisely to avoid that. - Underflow — results smaller than enter the subnormal range, where the leading 1 is given up so the gap to zero is filled evenly. Precision is lost gradually; below the result is 0.
- Inf and NaN — , NaN (not a number). NaN compares unequal to everything, itself included, and silently poisons every calculation it touches.
- Integer conversion — casting a float into a fixed-size integer can overflow too, and that is not always caught.
🛰Ariane 5, Flight 501 — 4 June 1996
Thirty-seven seconds after lift-off from Kourou, Ariane 5's first flight veered off course and broke up. The inertial reference software, reused from Ariane 4, converted a 64-bit floating-point value (the horizontal bias) into a 16-bit signed integer. Ariane 5 accelerated harder than Ariane 4, the value exceeded 32,767, and the conversion raised an operand error. The protection for that conversion had been removed to save processor time, on the grounds that Ariane 4's trajectory could never trigger it. Both inertial units failed the same way, within 72 milliseconds of each other. The inquiry board's report is short and worth reading in full.
💡Where you will use this
Every later course runs on doubles. GNSS Navigation: a pseudorange of 20,000 km stored with relative error is still good to 2 nm — but a receiver clock in single precision would not be. Orbital Mechanics and Attitude Control: flight software often runs in single precision to save power, so its developers do this lesson's error budget on every variable. Rocket Propulsion and Structures: nothing breaks until a value overflows or a NaN propagates through a solver at 3 a.m.
📚Further reading
- David Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic (1991) — the classic tutorial, free online.
- Michael Overton, Numerical Computing with IEEE Floating Point Arithmetic (SIAM, 2001) — a short book on exactly this lesson.
- Nicholas Higham, Accuracy and Stability of Numerical Algorithms (2nd ed., SIAM, 2002), chapter 2, 'Floating Point Arithmetic'.
- US General Accounting Office, Patriot Missile Defense: Software Problem Led to System Failure at Dhahran (IMTEC-92-26, 1992).
↓But there is a problem
So every stored number carries a relative error of at most about . That sounds like the end of the story: sixteen digits is far more than any measurement deserves.
But programs do not store one number. They add, subtract, multiply and divide millions of them, and each operation is rounded again.
In 1982 a stock exchange in Vancouver launched a new index, and every operation on it lost less than a thousandth of a point. Twenty-two months later the index had lost half its value — while the market it measured had gone up.