The real reason 0.1 + 0.2 doesn’t equal 0.3
Most programmers know that floating point arithmetic is inexact, and that 0.1 + 0.2 can produce 0.30000000000000004. What’s less obvious is why: there exist 64-bit floats closer to the true value of 0.3 than that answer, so why doesn’t IEEE 754 rounding pick one of those?
The short answer is that the true sum of the two floats nearest 0.1 and 0.2 sits exactly between the two floats nearest 0.3. When a result is perfectly midway between two representable values, the specification’s “round to nearest, ties to even” rule chooses the float whose significand is even.
Step 1: what 0.1 and 0.2 really are
64-bit floats are binary numbers, but every one of them can be written exactly in decimal — you just need enough digits. Python reveals the exact values:
>>> f"{0.1:.80f}"
'0.10000000000000000555111512312578270211815834045410156250000000000000000000000000'
>>> f"{0.2:.80f}"
'0.20000000000000001110223024625156540423631668090820312500000000000000000000000000'
Those are the precise decimal expansions of the two stored floats commonly labelled 0.1 and 0.2.
Step 2: add the exact values
Adding those two decimal expansions exactly gives:
>>> 1000000000000000055511151231257827021181583404541015625 + 2000000000000000111022302462515654042363166809082031250
3000000000000000166533453693773481063544750213623046875
That sum, 0.3000000000000000166533453693773481063544750213623046875, is not itself a representable 64-bit float. It needs rounding.
Step 3: inspect the neighbours
The two floats bracketing the sum are the one printed as 0.3 and the one printed as 0.30000000000000004. Their exact values are:
0.299999999999999988897769753748434595763683319091796875
0.3000000000000000444089209850062616169452667236328125
The sum from step 2 falls exactly halfway between those two candidates:
>>> (3000000000000000444089209850062616169452667236328125000 + 2999999999999999888977697537484345957636833190917968750) // 2 == 3000000000000000166533453693773481063544750213623046875
True
Step 4: ties resolve to the even significand
When a result is equidistant from two floats, the hardware rounds to the one whose significand is even. The hex representations show which is which:
0.30000000000000004comes from the byte pattern?\xd33333340.3comes from?\xd3333333
The last hex digit 4 is even, so the larger of the two neighbours wins. That’s why the addition returns 0.30000000000000004.
The same calculation in binary
The decimal walkthrough is easier to read, but the hardware works in base 2. A 64-bit float is defined by a sign bit, an exponent, and a 52-bit significand, representing:
sign × 2exponent × (1 + significand / 252)
Exponents and significands of 0.1 and 0.2
Extracting the exponent and significand for 0.1 and 0.2 (both positive, so the sign bit can be ignored) gives:
def get_exponent(f):
# get the first 12 bytes
bytestring = struct.pack('!d', f)
return int.from_bytes(bytestring, byteorder='big') >> 52
def get_significand(f):
# get the last 52 bytes
bytestring = struct.pack('!d', f)
x = int.from_bytes(bytestring, byteorder='big')
exponent = get_exponent(f)
return x ^ (exponent << 52)
For 0.1, the exponent is -4 and the significand is 2702159776422298:
>>> get_exponent(0.1) - 1023
-4
>>> get_significand(0.1)
2702159776422298
>>> 2**-4 + 2702159776422298 / 2**(52 + 4)
0.1
For 0.2, the exponent is -3 with the same significand. That’s no coincidence: x and 2x always share a significand, since doubling an exponent shifts the value by exactly one power of two.
>>> get_exponent(0.2) - 1023
-3
>>> get_significand(0.2)
2702159776422298
>>> 2**-3 + 2702159776422298 / 2**(52 + 3)
0.2
Aligning exponents before addition
To add the two values, the smaller exponent must be raised. Since 0.1 has exponent -4 and 0.2 has exponent -3, rewrite 0.1 as an equivalent fraction with exponent -3:
2**-4 + 2702159776422298 / 2**(52 + 4)
Solving for the numerator gives X = 251 + 2702159776422298 / 2, which evaluates to:
>>> 2**51 + 2702159776422298 //2
3602879701896397
Now the significands can be added directly:
>>> 2702159776422298 + 3602879701896397
6305039478318695
The sum 6305039478318695 exceeds the maximum 52-bit significand of 252 - 1, so the exponent must increase one more notch and the value renormalized.
2**-2 + 1801439850948199 / 2**(52 + 3)
That result has a factor of 2 in the denominator of the significand term, so it must be divided by 2. The problem: 1801439850948199 is odd, so the division lands exactly between two integers. Ties again round to the even significand.
>>> 1801439850948199 / 2
900719925474099.5
The rounded significand produces the final float:
>>> 0.1 + 0.2
0.30000000000000004
What printing does — and doesn’t show
Printing a float doesn’t display its exact value. Instead, routines like Python’s choose the shortest decimal string that uniquely identifies the stored float — the closest float to that printed string. That’s why the float whose exact value is 0.299999999999999988897769753748434595763683319091796875 prints as plain 0.3.
Efficient shortest-round-trip printing is a genuinely hard problem, with dedicated academic work such as “Printing Floating-Point Numbers Quickly and Accurately” (Steele & White, PLDI 1990) and related papers on printing floats correctly.
Not every language uses the same printing rules. PHP, for example, outputs 0.3 for 0.1 + 0.2, which looks like a different result. The arithmetic underneath is identical — evaluating (0.1 + 0.2) - 0.3 in PHP yields 5.5511151231258E-17, matching Python. PHP’s formatter is simply less rigorous: it prints 0.3 even when the stored float is not the closest representable float to that decimal.



