Why 0.1 + 0.2 isn't 0.3: how floating-point numbers work
Why JavaScript, Python and Java print 0.30000000000000004 for 0.1 + 0.2, explained with the IEEE 754 double format, and how to compare, round and handle money safely.
Type 0.1 + 0.2 into a browser console and you get 0.30000000000000004. It isn’t a JavaScript quirk: Python, Java, C#, and Go give the same answer. Nothing is miscalculating. These languages all store numbers the same way.
Computers store fractions in binary
In most languages, a number with a fractional part is stored in the IEEE 754 double-precision (binary64) format. JavaScript’s Number, Python’s float, and double in Java and C are all binary64. The 64 bits are split three ways.
| Part | Bits | Role |
|---|---|---|
| Sign | 1 | Positive or negative |
| Exponent | 11 | Which power of two to scale by |
| Significand (fraction) | 52 | The digits. The leading 1 is implied, so precision is effectively 53 bits |
In decimal, 1/3 never ends: 0.3333…. In binary, any fraction whose denominator isn’t a power of two never ends. 0.1 is 1/10, and 10 contains a factor of 5, so in binary it is 0.000110011001100… repeating forever. It has to be cut off at 52 bits and rounded, so the value actually stored is not 0.1.
| You write | The value actually stored |
|---|---|
| 0.1 | 0.1000000000000000055511151231257827021181583404541015625 |
| 0.2 | 0.200000000000000011102230246251565404236316680908203125 |
| 0.3 | 0.299999999999999988897769753748434595763683319091796875 |
0.1 and 0.2 come out slightly high; 0.3 comes out slightly low. The sum is rounded to the nearest binary64 value, which is not the one stored for 0.3 but the next one up.
0.1 + 0.2 = 0.3000000000000000444089209850062616169452667236328125
It prints as 0.30000000000000004 because languages show the shortest decimal that reads back as the same value. The same rule is why 0.1 prints as 0.1. It doesn’t mean the stored value is exactly 0.1.
How far integers stay exact
With 53 bits of precision, every integer up to 2⁵³ (9,007,199,254,740,992) is exact. That’s why JavaScript’s Number.MAX_SAFE_INTEGER is 9,007,199,254,740,991. Beyond that, gaps open between the integers a double can hold.
9007199254740992 === 9007199254740993 // true
This is why many APIs send 64-bit database IDs as strings in JSON. If you need big integers in JavaScript, use BigInt.
What to do in practice
1. Don’t compare with ==
Compare floating-point results by asking whether they are close enough.
import math
math.isclose(0.1 + 0.2, 0.3) # True (default relative tolerance 1e-9)
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON // true
Number.EPSILON (2⁻⁵², about 2.22×10⁻¹⁶) is the gap between doubles near 1. The gap grows with the magnitude, so for large values a tolerance proportional to the numbers (a relative tolerance) is safer.
2. Round once, when you display
Math.round(1.005 * 100) / 100 gives 1, not 1.01, because 1.005 is stored as 1.00499999…. Rounding at every step lets errors pile up. Keep full precision while calculating and round once for display with toFixed() or Intl.NumberFormat.
3. Don’t calculate money with floats
Amounts that must be exact in decimal are handled one of two ways:
- Store integers of the smallest unit: cents for dollars, yen for yen. Addition and subtraction are then exact.
- Use a decimal type: Python’s
decimal.Decimal, Java’sBigDecimal, orDECIMAL/NUMERICcolumns in a database.
Watch how you construct them. In Java, new BigDecimal(0.1) copies the already-rounded double and gives 0.1000000000000000055511…. Only new BigDecimal("0.1"), from a string, is exactly 0.1. In Python, likewise, pass a string: Decimal("0.1").
A trade-off, not a bug
Floating point gives up a little precision so that a huge range of values fits in 64 bits. For science, graphics, or sensor readings, where a tiny error doesn’t matter, it is fast and accurate enough. You only need to know the places where the error does matter (comparison, rounding, money) and avoid them.
The floating-point converter shows the sign, exponent and significand bits of any number in binary64 and binary32, along with the exact decimal value that gets stored. Try 0.1, 0.3 and 9007199254740993 to see everything above for yourself.
References
- IEEE Std 754-2019, IEEE Standard for Floating-Point Arithmetic
- ECMAScript specification (ECMA-262): the Number type and
Number::toString - Python documentation, “Floating-Point Arithmetic: Issues and Limitations”