Why 0.1 + 0.2 Is Not 0.3: IEEE 754 Floating Point, Bit by Bit
DDEVELOPER
DeveloperPublished: 11 min read

Why 0.1 + 0.2 Is Not 0.3: IEEE 754 Floating Point, Bit by Bit

Enter 0.1 + 0.2 in a JavaScript console and the result is 0.30000000000000004, not 0.3. Common Python and Java calculations using IEEE 754 binary64 produce the same stored value. This is not a language bug: it follows from converting decimal fractions into finite binary floating-point values and rounding them.

Japanese original published: 2026-05-30

Reproducing the result in binary64 environments

Implementations that round the same inputs to IEEE 754 binary64 and perform the same addition obtain the same stored result. The number of displayed digits can still vary by language and formatting rule.

// JavaScript (Node.js)
> 0.1 + 0.2
0.30000000000000004
> 0.1 + 0.2 === 0.3
false

# Python 3
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False

// Java
System.out.println(0.1 + 0.2);
// 0.30000000000000004

// C, when double is binary64
printf("%.20f", 0.1 + 0.2);
// 0.30000000000000004441

ECMAScript defines Number using IEEE 754-2019 binary64 values. Python documents that float maps to IEEE 754 binary64 on almost all platforms. The C standard does not require every double implementation to be binary64, so its implementation characteristics must be checked.

Why a finite decimal becomes a repeating binary fraction

A fraction can terminate in one base and repeat forever in another. In base 10, 1/3 repeats. In base 2, decimal 0.1 behaves similarly:

0.1 (base 10) = 0.0001100110011001100110011… (base 2)
0.2 (base 10) = 0.0011001100110011001100110…
0.3 (base 10) = 0.0100110011001100110011001…

A reduced fraction has a finite binary expansion if and only if its denominator is a power of two. Thus 1/2, 1/4, and 3/8 terminate in binary, while 1/10 retains a factor of 5 and repeats.

DecimalFractionBinary form
0.51/20.1 (finite)
0.251/40.01 (finite)
0.3753/80.011 (finite)
0.11/100.000110011… (repeating)

Inside IEEE 754 binary64: 1 + 11 + 52 bits

A normal binary64 value has one sign bit, an 11-bit exponent field, and a 52-bit stored fraction. The leading 1. of a normal significand is implicit, giving 53 bits of precision.

┌─┬─────────────┬──────────────────────────────────────────────────┐
│S│ exponent 11 │                  fraction 52                     │
└─┴─────────────┴──────────────────────────────────────────────────┘

normal value = (−1)^S × 1.fraction × 2^(exponent − 1023)

An all-zero exponent field represents signed zero when the fraction is zero and a subnormal number when it is nonzero. An all-one exponent field represents infinity or NaN.

0.1 as binary64
hex:  0x3FB999999999999A
bits: 0 01111111011 1001100110011001100110011001100110011001100110011010

exact stored value:
0.1000000000000000055511151231257827021181583404541015625

The repeating pattern is cut to the available precision and rounded, normally using round-to-nearest, ties-to-even. The independently rounded value of 0.3 is 0x3FD3333333333333, while 0.1 + 0.2 is 0x3FD3333333333334: one binary64 step apart, a difference of about 5.55 × 10−17.

binary16, binary32, binary64, and binary128

FormatTotal bitsExponent bitsStored fraction bitsDecimal equivalent of binary precision
binary1616510about 3.3 digits
binary3232823about 7.2 digits
binary64641152about 15.9 digits
binary12812815112about 34.0 digits

The decimal figures are p × log10(2), where p includes the implicit leading bit. They are not the same as the number of decimal digits guaranteed to survive every conversion or the digits required for decimal round trips.

0.1 in several formats
binary16: 0x2E66              (error about 2.44e-5)
binary32: 0x3DCCCCCD          (error about 1.49e-9)
binary64: 0x3FB999999999999A  (error about 5.55e-18)

Where floating-point differences matter

Equality checks

0.1 + 0.2 === 0.3       // false
0.1 + 0.1 + 0.1 === 0.3 // false
0.1 * 10 === 1          // true for this operation sequence

Exact comparison is correct for integers and cases where identical bit patterns are required. Approximate numerical results should instead use absolute and relative tolerances chosen for the scale and purpose of the calculation. One fixed Number.EPSILON test is not suitable for every magnitude.

Money and decimal rules

Store money in an appropriate smallest-unit integer or a decimal type, and define the rounding unit and tie-breaking rule. Decimal.from_float(0.1) shows the exact decimal value of the already-rounded binary64 number; it is not the same as constructing exact decimal Decimal("0.1").

Large integers

JavaScript Number can distinguish every integer only through Number.MAX_SAFE_INTEGER, 253 − 1. The value 253 is representable, but 253 + 1 collides with it. Consider BigInt or strings for IDs and nanosecond Unix timestamps beyond the safe range. A current microsecond Unix timestamp is still below that limit, so evaluate the actual range rather than the unit name alone.

NaN

NaN === NaN          // false
Number.isNaN(NaN)   // true
Number.isNaN("x")   // false

Prefer Number.isNaN() when coercion is not intended. The global isNaN() first converts its argument and can classify non-number inputs unexpectedly.

Practical comparison patterns

// JavaScript: combine absolute and relative tolerance
function nearlyEqual(a, b, relTol = 1e-9, absTol = 0) {
  const diff = Math.abs(a - b);
  return diff <= Math.max(absTol, relTol * Math.max(Math.abs(a), Math.abs(b)));
}

# Python
math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

The tolerances are domain requirements, not universal constants. Values near zero often need a nonzero absolute tolerance; large values usually need a relative tolerance. Accounting, geometry, simulation, and test assertions can require different rules.

Summary

  • Decimal 0.1 repeats in binary because its reduced denominator is not a power of two.
  • binary64 rounds 0.1 and 0.2 before the addition and rounds the result again.
  • 0.3 and 0.1 + 0.2 are adjacent binary64 values.
  • Exact equality, approximate comparison, decimal arithmetic, and integer arithmetic solve different problems.
  • Use tolerances and numeric types that match the actual domain.

References and sources

Editorial note

This article was prepared with AI assistance and reviewed by an editor before publication. It may still contain factual errors, interpretation mistakes, or outdated information. Check the cited primary sources or official documentation before making an important decision.

Related articles