Numbers in JavaScript: why 0.1 + 0.2 is not 0.3, and how to handle money
JavaScript numbers are 64-bit floating point. Learn why decimals drift, the safe integer limit, parseInt traps, BigInt, and how to store money in paise or cents.
Comes up in: JavaScript, Recruiter screen
Learn#
Meghna’s invoice adds ₹0.10 and ₹0.20 and shows ₹0.30000000000000004. The tax check total === 0.3 fails, and a refund is blocked.
Every JavaScript number is a 64-bit floating point value (IEEE 754). It has about 15–17 significant digits of precision, stored in binary. Many decimals, like 0.1, cannot be written exactly in binary, so they are rounded. Small errors appear when you add them.
So compare decimals with a tolerance, such as Number.EPSILON, never with ===.
Whole numbers are exact up to Number.MAX_SAFE_INTEGER (2⁵³ − 1). Past that safe integer limit, two different numbers can look equal.
For money, store whole paise or cents as integers, and format only for display with Intl.NumberFormat.
console.log(0.1 + 0.2, 0.1 + 0.2 === 0.3, Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);
console.log(Number.MAX_SAFE_INTEGER, 2 ** 53 === 2 ** 53 + 1, 2n ** 53n + 1n);
console.log((1.005).toFixed(2), parseInt("12px"), ["10", "10", "10"].map(parseInt));
const paise = 1999 + 2999;
console.log(new Intl.NumberFormat("en-IN", { style: "currency", currency: "INR" }).format(paise / 100));Output (verified)
0.30000000000000004 false true 9007199254740991 true 9007199254740993n 1.00 12 [ 10, NaN, 2 ] ₹49.98
For integers beyond the safe limit — IDs, cryptography — use BigInt, written with an n.
stored value
- 0.1 → 0.1000000000000000055…
result
empty
0.1 in binary repeats forever, like 1/3 in decimal. It is stored rounded to 53 bits.
Read the steps as text
- 0.1 in binary repeats forever, like 1/3 in decimal. It is stored rounded to 53 bits.
- 0.2 is stored rounded too: 0.2000000000000000111…
- Adding the two rounded values gives a sum that rounds to the double just above 0.3.
- In paise there are no fractions: 10 + 20 is exactly 30. Divide by 100 only to display.
Predict#
Predict first
What does this print?
console.log(["1", "2", "3"].map(parseInt));map passes (value, index), and parseInt takes (string, radix). So the calls are parseInt("1", 0) → 1 (radix 0 means “guess”), parseInt("2", 1) → NaN (no base 1), parseInt("3", 2) → NaN (“3” is not a binary digit). Use .map(Number) instead.
Interview angle#
Say it like this — about 30 seconds
“JavaScript numbers are IEEE 754 doubles. Decimals like 0.1 have no exact binary form, so they are rounded, and 0.1 plus 0.2 lands on the double just above 0.3. Compare floats with a tolerance relative to their size, using
Number.EPSILON. Integers are exact up to 2 to the 53 minus 1; beyond that, use BigInt. For money I store integers in paise or cents, do all math on those, and format withIntl.NumberFormat.”
Follow-up When would you use BigInt?
Follow-up Why does (1.005).toFixed(2) give '1.00'?
Follow-up How do you parse '08' or user input safely?
Number("08") → 8 and check Number.isNaN. If you use parseInt, always pass the radix: parseInt(s, 10).Practice#
On your own
Write formatMoney with Intl.NumberFormat (en-IN for INR, en-US for USD) and nearlyEqual with a tolerance that grows with the size of the numbers.
Done when
formatMoney(123456789, "INR")is"₹12,34,567.89"and"USD"gives"$1,234,567.89"; 5 paise is"₹0.05".nearlyEqual(0.1 + 0.2, 0.3)is true;nearlyEqual(1, 1.0001)is false; large values compare correctly too.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
Why is 0.1 + 0.2 !== 0.3?
0.1 and 0.2 cannot be stored exactly in binary, so their sum is rounded slightly above 0.3.
What is Number.MAX_SAFE_INTEGER?
2^53 − 1; above it, integers can no longer all be told apart.
How should an app store money?
As integers in the smallest unit (paise, cents); format only for display.
Can you add a BigInt and a number?
No. 1n + 1 throws a TypeError; convert explicitly.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
One number type, binary floating point. Fractions with a power-of-two bottom (0.5, 0.25) are exact; most decimal fractions are not. Integers are exact up to 2⁵³ − 1.
Check yourself: Is 0.5 + 0.25 exact in JavaScript?
Yes. Both are exact binary fractions.
How it works underneathSenior rounds
A double has 1 sign bit, 11 exponent bits and 52 fraction bits, with an implicit leading 1 — 53 bits of precision Source: ECMA-262: The Number type. Number.EPSILON is the gap between 1 and the next double, 2⁻⁵². NaN and ±Infinity are special values in the same format.
Check yourself: How many bits does a double use for the significand?
52 stored bits plus 1 implicit bit, so 53 bits of precision.
Edge cases and trapsWhere people slip
parseInt stops at the first invalid character and reads 0x as hex. BigInt division truncates: 7n / 2n is 3n. Math.sumPrecise (ES2026) adds a list without drift; support: Chrome 147, Firefox 137, Safari 26.2; not in Node.js yet Source: MDN: Math.sumPrecise().
Check yourself: What does parseInt('0x1A') return?
26 — parseInt reads the hex prefix when no radix is given.
In productionStaff-level answers
JSON.parse turns big IDs into rounded numbers, so APIs send 64-bit IDs as strings. Keep money in integer minor units end to end, store currency codes beside amounts, and format with Intl.NumberFormat using the user’s locale. For tax and interest, round once, at a defined step, with a documented rule.
Check yourself: How should a JSON API send a 64-bit ID to a browser?
As a string, so JSON.parse does not round it.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- Why is
0.1 + 0.2 !== 0.3? How do you compare floats or handle money? - When would you use BigInt?