Run this code in most programming languages:
# python
>>> 0.1 + 0.2
0.30000000000000004// javascript
> 0.1 + 0.2
0.30000000000000004// go
fmt.Println(0.1 + 0.2) // 0.30000000000000004This result is not a language bug. Most languages use the IEEE 754 floating-point format. The format stores an approximation for many decimal fractions.
That approximation can affect billing code, tests, and any calculation that needs exact values. To understand the result, start with binary numbers.
Why binary cannot store 0.1
Computers store floating-point values in base 2. Some fractions that end in base 10 repeat forever in base 2.
For example, 1/3 repeats in decimal:
1/3 = 0.33333333...
The same issue happens to 0.1 in binary. To convert a decimal fraction to binary, multiply its remainder by 2. Each whole-number part becomes one binary digit.
0.1 × 2 = 0.2 → 0
0.2 × 2 = 0.4 → 0
0.4 × 2 = 0.8 → 0
0.8 × 2 = 1.6 → 1 (subtract 1, carry 0.6)
0.6 × 2 = 1.2 → 1 (subtract 1, carry 0.2)
0.2 × 2 = 0.4 → 0 ← the sequence repeats
The binary value is 0.00011001100110011001100.... The bits 0011 repeat forever. A computer has finite memory, so it must round this value.
A fraction is exact in binary only when its reduced denominator has no prime factors other than 2. The denominator of
0.1is 10, which includes 5.
The IEEE 754 format
IEEE 754 defines how most CPUs store floating-point values. A 64-bit double has three fields:
┌─┬───────────┬──────────────────────────────────────────────────┐
│S│ exponent │ mantissa │
│1│ 11 bits │ 52 bits │
└─┴───────────┴──────────────────────────────────────────────────┘
63 62 52 51 0
The sign bit is 0 for a positive number and 1 for a negative number. The exponent sets the scale. The mantissa stores the significant binary digits.
IEEE 754 stores normalized values in this form:
value = (-1)^sign × 1.mantissa × 2^(exponent - 1023)
The leading 1 is implicit for normal values. A double therefore has 53 bits of precision: 52 stored bits and the implicit bit.
The stored value of 0.1
The binary form of 0.1 normalizes as follows:
0.0001100110011... = 1.100110011001100... × 2^(-4)
Its sign bit is 0. Its stored exponent is -4 + 1023, or 1019. The mantissa has only 52 bits, so the CPU rounds the repeating pattern.
The stored value is not exactly 0.1:
stored 0.1 = 0.1000000000000000055511151231257827021181583404541015625
The same happens to 0.2:
stored 0.2 = 0.200000000000000011102230246251565404236316680908203125
The CPU adds these stored values:
0.1000000000000000055...
+ 0.2000000000000000111...
= 0.3000000000000000444...
The language prints that result as 0.30000000000000004.
The first error occurs when the program stores
0.1. Addition makes the error visible.
Why languages give the same result
Python, JavaScript, Go, Java, C, C++, and Rust usually send floating-point operations to the CPU. The CPU floating-point unit follows IEEE 754 rules.
# python
>>> 0.1 + 0.2 == 0.3
False// go
fmt.Println(0.1 + 0.2 == 0.3) // false// javascript
0.1 + 0.2 === 0.3; // false// java
System.out.println(0.1 + 0.2 == 0.3); // falseDifferent languages can have different default types and display rules. They share the same core limitation when they use binary floating-point values.
When this caused real failures
Floating-point errors can accumulate. The Patriot missile system used a limited fixed-point value for tenths of a second. After about 100 hours of operation, its time error was about 0.34 seconds. A Scud missile could travel about 570 meters in that time. On February 25, 1991, a Scud struck a United States Army barracks in Dhahran, Saudi Arabia. The official investigation linked the failure to this time error.
The Vancouver Stock Exchange showed a different failure. It truncated its index to three decimal places after each update. Small losses accumulated over many updates. In 1982, the index fell from 1000 to about 524 even though the market had risen. The exchange recalculated the index and reset it.
Small numeric errors often grow slowly. Check repeated calculations with extra care.
What to use instead
1. Use fixed-point integers for money
Store the smallest currency unit as an integer. Store ₹10.50 as 1050 paisa, not as a float.
// WRONG — float, will rot eventually
price := 10.50 // float64
// RIGHT — integer paisa, always exact
priceInPaisa := int64(1050) // ₹10.50Payment systems use this model because integer arithmetic is exact within its range.
2. Use decimal types when base-10 precision matters
Decimal types store decimal values exactly. They are useful for money and other values that must match a decimal specification.
# python
from decimal import Decimal
Decimal("0.1") + Decimal("0.2") == Decimal("0.3") # True-- postgres: NUMERIC / DECIMAL is exact base-10 arithmetic
SELECT 0.1::NUMERIC + 0.2::NUMERIC = 0.3::NUMERIC; -- trueDecimal arithmetic is often slower than hardware floating-point arithmetic. That cost is usually acceptable for financial work.
3. Compare float values with a tolerance
Use a tolerance when a float value is an expected approximation.
// WRONG
if a == b { ... }
// RIGHT
const epsilon = 1e-9
if math.Abs(a - b) < epsilon { ... }Choose the tolerance for the value range and the required accuracy. A fixed tolerance is not correct for every domain.
The rule
Do not use binary floating-point values for money. Do not compare calculated floating-point values with == unless exact equality is part of the data model.
IEEE 754 is fast and useful for graphics, simulations, machine learning, and many scientific tasks. It was not designed to store an account balance exactly.
0.1 + 0.2 is not wrong mathematics. It is the correct result for rounded binary approximations of 0.1 and 0.2.