Floating Point: Why 0.1 + 0.2 Is Not 0.3
Type 0.1 + 0.2 into almost any language and you get 0.30000000000000004. Here is why IEEE 754 binary floating point does that — and how to handle it correctly.
Open a Python REPL, a Node console, or a Ruby prompt and type 0.1 + 0.2. You will not get 0.3. You will get 0.30000000000000004. This is not a bug in any one language — it is how nearly all hardware represents fractional numbers, and understanding it will save you from a class of subtle errors.
Numbers Are Stored in Binary, and Binary Has Limits
Most languages represent decimals using IEEE 754, the standard for binary floating-point arithmetic. A typical double (64-bit float) packs a number into three parts: a sign bit, an exponent, and a mantissa (also called the significand). Think of it as scientific notation in base 2: the value is roughly sign × mantissa × 2^exponent. With 64 bits, the mantissa gets 52 bits, giving you about 15 to 17 significant decimal digits of precision.
The catch is the base 2 part. Just as you cannot write 1/3 exactly in decimal — it is 0.3333… forever — you cannot write certain decimal fractions exactly in binary. A fraction has a finite binary representation only if its denominator is a power of two. So 0.5 (1/2) and 0.25 (1/4) are exact. But 0.1 is 1/10, and 10 is not a power of two, so in binary 0.1 becomes a repeating fraction:
0.1 (decimal) = 0.0001100110011001100... (binary, repeating forever)
Since the mantissa is finite, the machine stores the nearest representable value to 0.1, not 0.1 itself. The same happens to 0.2. Neither is the number you typed — each is off by a tiny amount.
Where the Stray Digits Come From
When you compute 0.1 + 0.2, you are not adding the true values. You are adding the two rounded approximations, then rounding the result again to fit back into a double. Those small errors happen to push the sum just above the stored value of 0.3.
>>> 0.1 + 0.2
0.30000000000000004
>>> (0.1 + 0.2) == 0.3
False
>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17
The leftover 5.5e-17 is the accumulated rounding error. With a single operation it is invisibly small, but errors can accumulate: a loop that adds 0.1 ten thousand times will drift measurably away from 1000. The more operations you chain, the more rounding compounds.
When Floats Are the Wrong Tool
Floating point is the right choice for measurements, physics, graphics, and machine learning — anywhere small relative error is acceptable and speed matters. It is the wrong choice when exactness is required, and money is the classic example. Charging a customer $0.30000000000000004 or watching a running total drift by a penny is unacceptable.
You have two solid alternatives:
- Integers in the smallest unit. Store money as a whole number of cents (or satoshis, or tenths of a cent) and only format to a decimal point for display.
30cents is exact;0.30dollars is not. - A decimal or fixed-point type. Many languages provide arbitrary-precision decimal types — Python’s
decimal.Decimal, Java’sBigDecimal, C#‘sdecimal, SQL’sNUMERIC/DECIMAL. These store numbers in base 10, so 0.1 + 0.2 is exactly 0.3, at the cost of speed.
The rule of thumb: if a human will count the result and care about the last digit, do not use a raw float.
FAQ
Is this a Python bug? It does not happen in my calculator app.
Does using float instead of double fix it?
Are integers and decimal types slower?
Related reading
2026-06-22
TCP vs UDP, Explained Through What Breaks When You Pick Wrong
TCP and UDP aren't interchangeable. We walk through the exact failure modes — head-of-line blocking, silent packet loss, Nagle delays — that show up when you pick the wrong transport.
2026-06-22
Write-Ahead Logging: How Databases Survive a Power Cut
How write-ahead logging keeps your data intact when the machine dies mid-write — the log-first rule, fsync, checkpoints, and why PostgreSQL and SQLite both rely on it.
2026-06-22
Backpressure, Explained Through a Queue That Won't Fall Over
What backpressure actually is, why an unbounded queue is a memory leak in disguise, and the four strategies a producer can take when a consumer falls behind.
2026-06-22
What a Bloom Filter Actually Saves You (and When It Lies)
A bloom filter trades a small false-positive rate for big memory savings. Here is the math behind the trade, where it pays off, and the failure mode that bites people.
2026-06-22
Idempotency, Explained Through the Retry That Doesn't Double-Charge
A practical look at idempotency keys: why a retried payment request shouldn't charge a card twice, how the pattern works, and where it quietly breaks in production.
Get the best tools, weekly
One email every Friday. No spam, unsubscribe anytime.