# Why 0.1 + 0.2 !== 0.3 in JavaScript (and in almost every language)

> How IEEE 754 floating-point numbers actually work, why this isn't a JavaScript-specific bug, and the safe ways to compare and store decimal numbers.

- Autore / Author: Diego Betto (https://diegobetto.com/about/)
- Pubblicato / Published: 2026-09-19
- URL: https://diegobetto.com/en/javascript-0-1-plus-0-2/
- Tag: javascript, numbers, floating point, ieee754

Open any JavaScript console and type this:

```js
0.1 + 0.2 === 0.3; // false
0.1 + 0.2; // 0.30000000000000004
```

It looks like a bug. It isn't — and it's not even specific to JavaScript. Python, Java, C, Go, Rust: every language using standard IEEE 754 double-precision floating-point numbers produces the exact same result. Understanding why turns this from a weird gotcha into a predictable, manageable fact about how computers represent numbers.

## How IEEE 754 doubles actually work

JavaScript's `Number` type stores every number — integer or decimal — as a 64-bit IEEE 754 double-precision float. That format represents numbers in binary, using a fixed number of bits for the fractional part. This works perfectly for numbers with a clean binary representation (`0.5`, `0.25`, `2`), but many decimal fractions that look "clean" in base 10 have no exact binary equivalent — the same way `1/3` has no exact finite representation in base 10 (`0.3333...`).

`0.1` in binary is an infinitely repeating fraction, just like `1/3` is in decimal. Since the format only has 64 bits, it gets rounded to the closest representable value — which is _extremely_ close to `0.1`, but not exactly `0.1`. Add two of these tiny rounding errors together (`0.1`'s and `0.2`'s), and the result no longer rounds back to exactly `0.3`.

```js
(0.1).toFixed(20); // "0.10000000000000000555"
(0.2).toFixed(20); // "0.20000000000000001110"
```

Neither value is exactly what it looks like — they're the closest doubles available, and the tiny errors compound.

## Why this isn't a bug worth "fixing"

This isn't sloppy engineering — it's an inherent trade-off of representing infinite real numbers in a fixed number of bits, present in essentially every mainstream programming language's default numeric type. The alternative (arbitrary-precision decimal arithmetic by default) would be dramatically slower for the vast majority of numeric code that doesn't need exact decimal precision.

## Comparing floating-point numbers safely

Never compare floats for exact equality when they've been through arithmetic. Instead, check whether the difference is smaller than an acceptably tiny threshold:

```js
function nearlyEqual(a, b, epsilon = Number.EPSILON) {
  return Math.abs(a - b) < epsilon;
}

nearlyEqual(0.1 + 0.2, 0.3); // true
```

`Number.EPSILON` is the smallest difference JavaScript can represent between `1` and the next representable number above it — a reasonable default tolerance for comparisons near that magnitude, though for numbers far from `1` you may need a larger, scaled tolerance.

## Money: the case where this actually matters

The floating-point rounding error above is usually invisible in everyday code, but it becomes a real problem the moment you're summing prices, calculating totals, or doing anything involving currency, where users will notice a cent missing.

```js
// Fragile — accumulated rounding error across many additions
let total = 0;
[19.99, 5.0, 3.5].forEach((price) => (total += price));
console.log(total); // 28.49 today, but this class of bug compounds badly at scale
```

The standard fix is to work in the smallest currency unit (cents, not dollars) using integers, which don't have this rounding problem at all:

```js
// Robust — integers have no floating-point rounding issue
const cents = [1999, 500, 350];
const totalCents = cents.reduce((sum, c) => sum + c, 0);
console.log(totalCents / 100); // 28.49, computed exactly
```

For anything beyond simple sums — multi-currency conversion, tax calculations with many decimal places — reach for a dedicated decimal arithmetic library (like `decimal.js` or `big.js`) instead of hand-rolling cent-based math everywhere.

> `toFixed(2)` alone does not fix this — it only rounds the *displayed string*, the underlying
> number still carries the accumulated error, which can resurface the next time you do arithmetic on
> it. Fix the representation (integers, or a decimal library), not just the formatting.

## FAQ

> **Does this affect integers too?**
>
> Not for the range where it matters practically — integers up to 2^53 (`Number.MAX_SAFE_INTEGER`)
> are represented exactly in a double. The rounding error above is specific to fractional decimal
> values that don't have an exact binary representation.

> **Can BigInt fix this?**
>
> `BigInt` solves a related but different problem — arbitrary-precision *integers*, not decimals. It
> doesn't have a fractional part at all, so it's not a drop-in fix for money math, though
> representing currency as integer cents (as shown above) pairs naturally with it for very large
> sums.

> **Is this fixed by Temporal or other newer JS features?**
>
> No — Temporal solves date/time correctness, a separate problem from decimal arithmetic. There's a
> separate TC39 proposal for a native `Decimal` type, but as of now it hasn't shipped, so
> integer-based math or a library remains the practical fix.

## Conclusion

`0.1 + 0.2 !== 0.3` isn't a JavaScript quirk — it's a direct, predictable consequence of representing decimal fractions in binary floating point, shared by nearly every mainstream language. Never compare floats for exact equality after arithmetic; use an epsilon-based comparison instead, and for money specifically, work in integer cents or reach for a decimal library rather than trusting `toFixed()` to paper over the underlying representation.

**References**

- [IEEE 754 – Wikipedia](https://en.wikipedia.org/wiki/IEEE_754)
- [Number.EPSILON – MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON)
- [0.30000000000000004.com — the same issue across every language](https://0.30000000000000004.com/)
