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.

DevelopmentNumbers

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’s BigDecimal, or DECIMAL/NUMERIC columns 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”

Back to the blog