Type 0.1 + 0.2 into almost any language and you get 0.30000000000000004.
This is the most reported non-bug in computing. The floating-point unit is working
perfectly; the problem is that you asked it to store a number it physically
cannot represent.
Computers Count in Binary Fractions
A float stores numbers as binary fractions — sums of powers of two:
0.5 = 1/2 = 0.1 in binary
0.75 = 1/2 + 1/4 = 0.11 in binary
0.625 = 1/2 + 1/8 = 0.101 in binaryAny value that’s a sum of powers of two fits exactly. The trouble: 0.1 is not. In binary it’s a repeating fraction, like 1/3 is in decimal:
0.1 (decimal) = 0.0001100110011001100110011… (binary, forever)The hardware has finite bits (53 of significand in a 64-bit double), so it stores the closest representable value — slightly off. Add two slightly-off numbers and the errors surface in the last digits.
IEEE 754 in One Picture
A double splits its 64 bits like scientific notation in binary:
┌─┬───────────┬────────────────────────────────────┐
│S│ exponent │ significand │
│1│ 11 bits │ 52 bits │
└─┴───────────┴────────────────────────────────────┘
sign scale (×2^e) the fractional digits
value = (-1)^S × 1.significand × 2^(exponent − bias)Roughly 15–17 significant decimal digits of precision. Plenty — until you assume it’s exact.
The Rules That Follow
Never test floats for equality. 0.1 + 0.2 == 0.3 is false. Compare within
a tolerance instead:
if (fabs(a - b) < 1e-9) // "close enough", not exact(Real code should scale the tolerance to the magnitude of the values — an absolute epsilon is wrong for very large or very small numbers.)
Precision degrades with magnitude. The 53 bits are relative, so big numbers
have coarse spacing. Past 2^53, integers themselves stop being representable —
9007199254740993.0 rounds to an even neighbor.
Don’t use floats for money. Repeated rounding accumulates. Use integer cents, or a decimal type that represents base-10 fractions exactly.
When You Need Exactness
- Money / accounting → integers (cents) or a decimal library.
- Comparisons → tolerance-based, scaled to magnitude.
- Exact rationals → a big-rational type that stores numerator/denominator.
Floats are a deliberate trade: enormous range and speed in exchange for exactness. For physics and graphics that’s perfect; for a ledger it’s a liability.
Takeaways
- Floats store binary fractions; decimals like 0.1 don’t fit, so they’re rounded to the nearest representable value.
- IEEE 754 gives ~15–17 significant digits — relative precision, so large numbers are spaced coarsely (integers break past 2^53).
- Never compare floats with
==; use a magnitude-scaled tolerance. - Use integers or a decimal type for money — never binary floats.