Skip to content
All posts

Why 0.1 + 0.2 !== 0.3 (and what to do with money)

IEEE-754 explained in 500 words, why every language does this, and the three correct ways to handle currency.

FDFinance DeskFinance & Numeracy EditorPublished April 26, 20264 min readintermediate

# The famous bug


> 0.1 + 0.2
0.30000000000000004
> 0.1 + 0.2 === 0.3
false

This isn't a JavaScript bug. Python does it. C does it. Go does it. Every language using IEEE-754 doubles — which is every modern language — has this behavior.

# Why it happens

Binary floating point can represent exactly any fraction of the form a / 2ⁿ. It cannot represent 0.1 exactly, because 0.1 in binary is a repeating fraction:


0.1₁₀ = 0.0001100110011001100110011…₂

Just like 1/3 is 0.333… in decimal. Finite digits = rounding error.

A JavaScript number stores 52 bits of mantissa. When you type 0.1, you actually get the number closest to 0.1 that the 52-bit mantissa can represent — which is slightly off. Add two slightly-off numbers, you get a slightly-off sum.

# Why we live with it

Floats trade exactness for speed and range. A double holds numbers from ~10⁻³⁰⁸ to ~10³⁰⁸, fast. Getting exact decimals would require arbitrary-precision arithmetic, which is 100× slower.

For scientific computing, engineering, machine learning — speed wins. For money, you need a different approach.

# Money, done right

# Option 1: Store as integer minor units

$1.23123 cents.


const total = 123 + 456 + 789; // 1368 cents = $13.68
const formatted = (total / 100).toFixed(2); // "13.68"

Integer arithmetic is exact. You only convert to a display string at the UI boundary, never for computation.

Works for any currency. 1 JPY = 1 minor unit. 1 KWD = 1000 minor units (3-decimal currency).

# Option 2: Arbitrary-precision decimals

In JavaScript: BigInt (integer-only), or libraries like decimal.js / dinero.js.


import Decimal from "decimal.js";
new Decimal("0.1").plus("0.2").toString(); // "0.3"

Slower but closer to mathematical intent. Use when you're doing lots of division or percentages.

# Option 3: Don't compound the error

For simple additive arithmetic on a bounded scale (prices in a cart), even floats give correct results if you round at the end:


const total = 19.99 + 4.50 + 2.75;
Math.round(total * 100) / 100; // 27.24

Works until it doesn't. Compound interest over 30 years or iterative divisions — use Option 1 or 2.

# What databases store

  • PostgreSQL: NUMERIC(19,4) is the classic money column. Exact, 19 digits of precision.
  • MySQL: DECIMAL(15,2). Same story.
  • SQLite: no native type; use INTEGER (minor units) or TEXT (string).
  • DynamoDB: Number is string-backed, effectively arbitrary-precision. Safe.

Never use FLOAT or DOUBLE for a money column. Ever.

<div class="callout callout-danger" role="note"><div class="callout-title">Danger</div><div class="callout-body"><p>The single worst bug we've seen in code review: a <code>FLOAT</code> money column summed across 10,000 rows. The rounding errors accumulated to several hundred dollars over a month. The engineer couldn't reproduce it locally because 100 rows didn't drift far enough to notice.</p></div></div>

# What our calculators do

Our Currency Converter, Tip Calculator, VAT Calculator, and other finance tools use JavaScript Number — but round at every step and display with fixed decimal places. For a $1000 tip split 4 ways, this is fine.

If you're writing ledger software, don't imitate us. Use Option 1.

Frequently asked questions

Is this a JavaScript bug?

No. Every language with IEEE-754 doubles — Python, C, Go, Rust, Java — does the same thing with 0.1 + 0.2. It's not a language flaw; it's the unavoidable cost of representing decimals in binary floating point.

Does this actually matter for my app?

It matters when: (a) you store money as a float, (b) you round at display time and the error accumulates over many rows, (c) you compare floats with `===`. For anything else, it's invisible.

New posts, once a week.

Practical developer guides. No spam. Unsubscribe any time.

Tools mentioned

Keep reading