Why 0.1 + 0.2 !== 0.3 (And What To Do About It)
JS Edition
March 20, 2026

Type this into your browser console:
0.1 + 0.2
You get: 0.30000000000000004
Not 0.3. Not even close. Off by 0.00000000000000004.
This isn’t a JavaScript bug. This is how computers work. And it breaks shopping carts, financial calculations, and junior developers’ brains on a regular basis.
Let me explain why your computer can’t do elementary school math.
The Problem is Universal
Before we blame JavaScript, let’s be clear: every programming language has this problem.
Python:
0.1 + 0.2 # 0.30000000000000004
Java:
0.1 + 0.2 // 0.30000000000000004
C:
0.1 + 0.2 // 0.30000000000000004
Ruby:
0.1 + 0.2 # 0.30000000000000004
It’s not the language. It’s IEEE 754, the floating-point standard that all modern computers use.
And yes, it’s been like this since 1985. We’ve just accepted that computers can’t add.
Why Computers Are Bad At Decimals
Computers store numbers in binary (base 2). You know: 0s and 1s.
Decimal 0.1 in binary is:
0.0001100110011001100110011001100110011001100110011001...
It repeats forever. Like how 1/3 = 0.333333... in decimal.
But floating-point numbers have limited space. JavaScript uses 64 bits (IEEE 754 double precision). So the computer has to round.
0.1 → 0.1000000000000000055511151231257827021181583404541015625\n0.2 → 0.2000000000000000111022302462515654042363166809082031250
These aren’t exactly 0.1 and 0.2. They’re the closest representable values.
Add them together:
0.30000000000000004440892098500626161694526672363281250
Which displays as: 0.30000000000000004
Your computer didn’t mess up. It just can’t store 0.1 exactly.
The Practical Disasters
This isn’t academic. Real code breaks because of this:
Shopping cart math:
let total = 0;\nitems.forEach(item => {\n total += item.price; // 0.1, 0.2, 0.3...\n});\n\n// total is now 0.6000000000000001 instead of 0.6\n// Display to user: "$0.60" (after rounding)\n// Send to payment processor: 0.6000000000000001\n// Payment processor: "Invalid amount"
Comparison hell:
const price = 0.1 + 0.2;\n\nif (price === 0.3) {\n applyDiscount(); // Never runs\n}\n\n// Because:\nconsole.log(price === 0.3); // false
Accumulation errors:
let balance = 0;\nfor (let i = 0; i < 1000; i++) {\n balance += 0.1;\n}\n\nconsole.log(balance); // 100.00000000000001\n// Should be exactly 100
After 1000 additions, you’re off by one hundredth of a cent. In a financial system with billions of transactions? That adds up to real money.
Why It’s Worse Than You Think
Multiplication makes it weirder:
0.1 * 0.2 // 0.020000000000000004
Division is a minefield:
0.3 / 0.1 // 2.9999999999999996 (not 3!)
Even integers break eventually:
9007199254740992 + 1 // 9007199254740992 (didn't add!)
Once you pass Number.MAX_SAFE_INTEGER (2^53 - 1), integers lose precision too.
This one broke Twitter:
const tweetId = 9007199254740993; // Tweet ID from API\nconsole.log(tweetId); // 9007199254740992 (different number!)
Twitter IDs went past the safe integer limit. JavaScript couldn’t store them accurately. Tweets disappeared. The API started returning IDs as strings.
The “Just Use toFixed” Solution (That Doesn’t Work)
Everyone’s first solution:
(0.1 + 0.2).toFixed(2) // "0.30"
Great! Problem solved, right?
Wrong.
const result = (0.1 + 0.2).toFixed(2); // "0.30"\ntypeof result // "string"\n\nresult === 0.3 // false (string vs number)\nresult == 0.3 // true (coerces string to number)\n\n// And then:\nresult + 0.1 // "0.300.1" (string concatenation!)
toFixed() returns a string. Now you have string vs number problems on top of floating point problems.
Also:
1.005.toFixed(2) // "1.00" (should be "1.01")
toFixed() uses “round half to even” (banker’s rounding), which is correct mathematically but surprising in practice.
The Epsilon Comparison (The Real Solution)
Instead of checking if two numbers are equal, check if they’re “close enough”:
function almostEqual(a, b) {\n return Math.abs(a - b) < Number.EPSILON;\n}\n\nalmostEqual(0.1 + 0.2, 0.3) // true
Number.EPSILON is the smallest difference between two representable numbers: 2.220446049250313e-16
If two numbers differ by less than that, they’re “equal enough” for most purposes.
Better version with configurable tolerance:
function almostEqual(a, b, epsilon = Number.EPSILON) {\n return Math.abs(a - b) < epsilon;\n}\n\n// For money (2 decimal places), use larger epsilon:\nalmostEqual(0.1 + 0.2, 0.3, 0.01) // true
This is what game engines, physics simulations, and graphics libraries do.
The Integer Solution (For Money)
Store money as cents, not dollars.
// Bad:\nlet price = 19.99;\nlet tax = price * 0.08; // 1.5992000000000001\nlet total = price + tax; // 21.589200000000002\n\n// Good:\nlet priceInCents = 1999; // 19.99 * 100\nlet taxInCents = Math.round(priceInCents * 0.08); // 160\nlet totalInCents = priceInCents + taxInCents; // 2159\n\n// Display:\nconst total = totalInCents / 100; // 21.59
Integers are exact up to Number.MAX_SAFE_INTEGER. No rounding errors.
Stripe does this. Their API uses cents:
stripe.charges.create({\n amount: 2000, // $20.00 in cents\n currency: 'usd'\n});
PayPal does this. Their API uses cents.
Your shopping cart should do this.
The Decimal Library Solution
For complex financial calculations, use a library:
decimal.js:
import Decimal from 'decimal.js';\n\nconst a = new Decimal(0.1);\nconst b = new Decimal(0.2);\nconst result = a.plus(b); // Decimal object\n\nresult.toString() // "0.3" (exact!)\nresult.toNumber() // 0.3
big.js:
import Big from 'big.js';\n\nconst a = new Big(0.1);\nconst b = new Big(0.2);\nconst result = a.plus(b); // Big object\n\nresult.toString() // "0.3"
bignumber.js:
import BigNumber from 'bignumber.js';\n\nconst a = new BigNumber(0.1);\nconst b = new BigNumber(0.2);\nconst result = a.plus(b);\n\nresult.toString() // "0.3"
These libraries use string-based arithmetic. Slower than native numbers, but exact.
The BigInt Solution (For Integers Only)
JavaScript added BigInt for arbitrary-precision integers:
const big = 9007199254740993n; // Note the 'n'\nconst bigger = big + 1n; // 9007199254740994n\n\n// Twitter IDs as BigInt:\nconst tweetId = 9007199254740993n;\nconsole.log(tweetId); // 9007199254740993n (exact!)
But BigInt doesn’t help with decimals:
const a = 0.1n; // Error: Cannot convert 0.1 to a BigInt
BigInt is for integers only. Use it for:
-
Database IDs
-
Timestamps
-
Cryptocurrency amounts (stored as smallest unit)
-
Large integers from APIs
Don’t use it for money with decimal places.
When Floating Point is Fine
Not everything needs precision:
Game development:
player.x += velocity * deltaTime;\n// Close enough for 60 FPS
Data visualization:
const average = total / count;\n// Chart doesn't need perfect precision
UI animations:
element.style.left = position + 'px';\n// Pixels are integers anyway
Scientific calculations:
const distance = Math.sqrt(dx*dx + dy*dy);\n// Measurements have error anyway
Floating point is fast. Native CPU operations. If you don’t need exact decimal precision, use it.
The Real Rules
Use floating point when:
-
Doing graphics/game math
-
Scientific calculations
-
Performance matters
-
Exact decimal precision doesn’t
Use integers (cents) when:
-
Dealing with money
-
You need exactly 2 decimal places
-
Simple arithmetic only
Use decimal libraries when:
-
Complex financial calculations
-
Need arbitrary precision
-
Can’t use integer math
-
Correctness > performance
Use BigInt when:
-
Need integers beyond 2^53
-
Database IDs
-
Timestamps in nanoseconds
-
Cryptocurrency (store as satoshis/wei)
The Mental Model
Think of floating point like measuring with a ruler:
You can’t measure 0.1 inches exactly. You get “about 0.1 inches.” Close enough for carpentry, but not for watchmaking.
Computers are the same. 0.1 in binary is like 1/3 in decimal - it repeats forever. The computer picks “close enough.”
For most tasks, close enough is fine. For money, it’s not fine. That’s when you switch to precise tools.
The Comparison That Actually Works
Never do this:
if (price === 0.3) {\n // This will fail\n}
Do this:
// For general floating point:\nif (Math.abs(price - 0.3) < Number.EPSILON) {\n // Probably works\n}\n\n// For money (2 decimal places):\nif (Math.round(price * 100) === Math.round(0.3 * 100)) {\n // This works\n}\n\n// Or just use cents:\nif (priceInCents === 30) {\n // Best option\n}
Why We Can’t Fix This
“Why doesn’t JavaScript just use exact decimals?”
Because it would be slow. IEEE 754 floating point uses hardware. It’s fast because your CPU has dedicated floating-point units.
Exact decimal arithmetic requires software. It’s 10-100x slower.
Languages with built-in decimal types (Python’s Decimal, Java’s BigDecimal) are slow for math. That’s why they also have float/double for fast approximate math.
JavaScript chose speed by default. Want precision? Use a library.
The Classic Fails
These will never not be funny:
// The trilogy:\n0.1 + 0.2 === 0.3 // false\n0.1 + 0.2 === 0.30000000000000004 // true\nMath.floor(0.1 + 0.2) // 0 (not 1!)\n\n// The sequel:\n0.3 - 0.2 === 0.1 // false\n0.3 - 0.1 === 0.2 // true (sometimes it works!)\n\n// The twist:\n9.7 + 1.3 // 11 (this one works)\n9.7 + 1.3 === 11 // true (wait, what?)\n\n// The finale:\n0.1 * 0.1 // 0.010000000000000002\nMath.sqrt(0.01) // 0.09999999999999999 (not 0.1!)
Floating point is chaos. Embrace it.
How Other Languages Handle This
Python:
from decimal import Decimal\n\nDecimal('0.1') + Decimal('0.2') # Decimal('0.3')
Built-in, but you have to opt in.
Rust:
// No built-in decimal, use rust_decimal crate\nuse rust_decimal::Decimal;\n\nlet a = Decimal::new(1, 1); // 0.1\nlet b = Decimal::new(2, 1); // 0.2\nlet c = a + b; // 0.3 (exact)
Java:
import java.math.BigDecimal;\n\nBigDecimal a = new BigDecimal("0.1");\nBigDecimal b = new BigDecimal("0.2");\nBigDecimal c = a.add(b); // 0.3
Most languages have decimal libraries. Few make them the default because performance.
My Honest Take
I’ve been burned by floating point math exactly once in production. Shopping cart totals didn’t match payment processor amounts. Took three hours to debug.
The fix? Store everything in cents. Problem disappeared.
Most developers will never hit this bug because:
-
They don’t do financial calculations
-
They round for display anyway
-
Small errors don’t matter in their domain
But when you DO hit it, it’s confusing and infuriating because your elementary school math teacher lied to you. Computers can’t do basic arithmetic.
The solution is simple: don’t use floating point for money. Use integers (cents). If you need arbitrary precision, use a decimal library.
For everything else? Floating point is fine. Your game’s physics don’t need perfect precision. Your chart doesn’t care about 0.00000000000000004.
Just remember: 0.1 + 0.2 will never equal 0.3 in JavaScript. Or Python. Or any language using IEEE 754.
And that’s okay.
Try it yourself:
// The floating point hall of shame:\nconsole.log(0.1 + 0.2); // 0.30000000000000004\nconsole.log(0.3 - 0.2); // 0.09999999999999998\nconsole.log(0.2 + 0.4); // 0.6000000000000001\nconsole.log(0.3 / 0.1); // 2.9999999999999996\n\n// The integer solution:\nconst a = 10; // 0.1 in cents (actually 0.10)\nconst b = 20; // 0.2 in cents (actually 0.20)\nconsole.log(a + b); // 30 (exactly!)\nconsole.log((a + b) / 100); // 0.3 (perfect)\n\n// You're welcome.
Thanks for reading Under The Hood! Subscribe for free to receive new posts and support my work.
Thanks for reading Under The Hood! This post is public so feel free to share it.