№ 52 · computing

Why 0.1 + 0.2 is not 0.3

Ask almost any programming language whether 0.1 + 0.2 equals 0.3 and it says no. The computer is not broken. It is doing exact arithmetic on numbers not quite the ones you typed.

A number in 64 bits

A floating-point number is scientific notation in binary: a sign, a string of significant digits, and an exponent that says where the point goes. The common 64-bit format has one sign bit, an 11-bit exponent, and 52 stored bits of digits. Because a normalised binary number always starts with a 1, that leading 1 is not stored — Goldberg calls it the hidden bit — so the format carries 53 significant bits in 52 bits of space. Every value a program holds is one of these patterns, and between neighbouring patterns there is nothing.

Why it matters

Prices, coordinates, sensor readings and probabilities all pass through this format. Usually the rounding is harmless. But a test like x == 0.3 can be false when every step was done correctly, and a loop that adds 0.1 until it "reaches" 1 may never stop. Once you can see which values exist and which do not, these stop being mysteries and become predictable.

Interactive Type any decimal into x to see the 64-bit pattern it is actually stored as and how far that pattern sits from what you typed; switch to sum, keep 0.1 and 0.2 or pick your own pair, and compare the bits of the computed sum with the bits of the number you meant.

■ sign (1 bit)■ exponent (11 bits)■ fraction (52 bits)value = (−1)sign × 1.fraction × 2exponent − 1023 (an all-zero exponent field means 0.fraction × 2−1022: zero and the denormals)
Every digit shown is computed from the bits, not from a decimal print routine: the 64-bit pattern is read out of memory, and its exact value is reconstructed with integer arithmetic as fraction × 2exponent, which always has a finite decimal expansion. The error is the exact difference between what you typed and what was stored, and the ulp is the distance to the next pattern at that size. In sum mode the two rounded inputs are added exactly, the halfway case is visible, and the underlined bit is where the computed sum and the intended number part company.

Where 0.1 goes

In decimal, 1/3 is 0.333… forever, so any finite number of digits is slightly wrong. The same happens to 0.1 in binary. Goldberg's example: in base 2, "the decimal number 0.1 cannot be represented exactly" — it has an infinite repeating binary expansion, 0.0001100110011…, so it "lies strictly between two floating-point numbers and is exactly representable by neither of them." The machine stores the nearest one. In the 64-bit format that is 0.1000000000000000055511151231257827021181583404541015625, a little above 0.1. The distance from a real number to the nearest pattern is measured in ulps, "units in the last place"; when a value is rounded to the nearest floating-point number, the error is at most half an ulp.

0.2 is likewise stored a little high. Now add them. The IEEE standard requires that addition be computed exactly and then rounded to the nearest floating-point number, with ties broken by round to even — the result whose last bit is 0. The exact sum of the two stored values is 0.30000000000000001665334536937…, which sits exactly halfway between two neighbouring patterns. The tie rule picks the even one, 0.3000000000000000444089209850062616169452667236328125. The nearest pattern to the typed constant 0.3 is the other neighbour, 0.299999999999999988897769753748434595763683319091796875, one ulp lower. Two different bit patterns, so the comparison is false — and printed with enough digits to tell them apart, the sum reads 0.30000000000000004.

How small is the gap

Near 1, the next pattern up is 1 + 2−52, about 2.2 × 10−16 away. Goldberg calls the bound on relative rounding error machine epsilon: rounding to the nearest number is never off by more than that fraction of the value, and for this format it is half that gap, 2−53. The gap scales with the number: near 0.3 the patterns are 2−54 apart, near a million they are about 10−10 apart. So the error is relative, sixteen or so decimal digits deep, at any magnitude.

That is why the usual fix is to test not equality but closeness: whether two values differ by less than a tolerance sized to the numbers involved. And when the quantity is decimal by nature — money above all — a decimal type stores 0.1 exactly and the question never arises.

In short

A double is a 64-bit pattern: a sign, an exponent, and 53 significant binary digits. 0.1 and 0.2 have no finite binary expansion, so each is stored as its nearest pattern, both a little high. Their exact sum lands halfway between two patterns and the tie rule picks the upper one, while 0.3 alone rounds to the lower one. The arithmetic was exact at every step; the inputs had already moved.

Where this comes from

  1. What Every Computer Scientist Should Know About Floating-Point Arithmetic (ACM Computing Surveys 23(1)) linked only, not reproduced
    David Goldberg · 1991
    docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html