JavaScript is not miscalculating. Every Number is a 64-bit binary floating-point value, and the decimal fraction 0.1 has no finite exact form in binary. The two stored approximations add to 0.30000000000000004, which is not strictly equal to 0.3, so 0.1 + 0.2 === 0.3 returns false. The right fix depends on what your code does with the result: display it, compare it, store money, or follow specific decimal rounding rules. Each of those needs a different remedy.
Why the stored value is slightly off
JavaScript stores numbers in base 2. Some decimal fractions terminate cleanly in base 2 (0.5 is 2-1, and 0.25 is 2-2), but most do not. 0.1 is one of those. The double-precision value that JavaScript keeps for 0.1 is about 0.1000000000000000055511, and the one for 0.2 is about 0.2000000000000000111022. When you add them, the exact sum is rounded to the nearest representable double, and the nearest one printed in shortest form is 0.30000000000000004. The double that 0.3 maps to is a different value, so the strict equality check fails.
As an Amazon Associate I earn from qualifying purchases.
This is not specific to JavaScript. Python, Java, C, and most other languages that use IEEE 754 doubles produce the same result. MDN’s documentation on Number and floating-point arithmetic describes this as a property of the representation rather than a defect in any calculator.
Reproduce it in the console
0.1 + 0.2; // 0.30000000000000004
0.1 + 0.2 === 0.3; // false
0.1 * 3; // 0.30000000000000004
Once you see that the error comes from the stored value, the important question is where in your program the value is used. An error of about 5.6 × 10-17 is invisible on a screen that shows two decimal places, but it will break an equality test or accumulate across thousands of additions.
#1 Best Overall
The four fixes
These remedies solve different problems. Pick one based on the requirement, not on which one appears first in a search result.
1. Format the value at the display boundary
If the number is only being shown to a user, format it when you render it. toFixed(digits) returns a string with a fixed number of decimal places, and Intl.NumberFormat returns a locale-aware string.
Rank #2
const sum = 0.1 + 0.2;
sum.toFixed(2); // "0.30" (a string)
Number(sum.toFixed(2)); // 0.3 (a number again)
new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
}).format(sum); // "$0.30"
Formatting changes only what is shown. sum still holds 0.30000000000000004, and sum === 0.3 is still false. Also be aware that toFixed rounds the binary value, not the decimal you typed. For example, (1.005).toFixed(2) returns "1.00" because 1.005 is stored as a value slightly below 1.005. If you need control over rounding mode, use the rounding options of Intl.NumberFormat, and check the current browser and Node.js support for the options you choose.
Recommended Free Tools
2. Store fixed-scale quantities as integers
For values with a known fixed scale, such as money in cents, store integer units and convert only at the edges. Integer arithmetic within the safe range is exact in a Number.
const priceCents = 1999; // $19.99
const quantity = 3;
const totalCents = priceCents * quantity; // 5997, exact
const display = (totalCents / 100).toFixed(2); // "59.97"
Integer units only work if every operation keeps the same scale. Multiplying cents by a percentage such as tax produces fractions, so you must decide how to round them, for example with Math.round, and document that choice. Integers are exact only up to Number.MAX_SAFE_INTEGER (253 − 1). If your totals may exceed that, use BigInt:
const big = 12345678901234567890n + 1n; // BigInt literal with the n suffix
const half = 7n / 2n; // 3n: BigInt division truncates toward zero
// 1n + 1 throws a TypeError because BigInt and Number cannot be mixed
BigInt holds only integers. It cannot represent 0.5, so you must still choose a scale (for example, tenths of a cent) and apply it consistently.
Rank #4
3. Compare with a tolerance
When the value is inherently approximate, such as the result of a measurement, a series of floating-point operations, or a numerical method, do not test it with ===. Test whether the difference is within a tolerance. MDN’s documentation on Number.EPSILON states that it is 2-52, approximately 2.2204460492503130808 × 10-16, which is the gap between 1 and the next representable number.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteMath.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true, the difference is about 5.6e-17
A fixed absolute cutoff is only correct near magnitude 1. The gap between adjacent doubles grows with the size of the value. Near 1000 it is about 1.1 × 10-13, which is far larger than Number.EPSILON, so an absolute check against Number.EPSILON can reject values that are only one representation step apart. A relative tolerance scales with the operands:
Best Value
function nearlyEqual(a, b, relTol = 1e-9, absTol = 1e-12) {
const diff = Math.abs(a - b);
const scale = Math.max(Math.abs(a), Math.abs(b));
return diff <= Math.max(absTol, relTol * scale);
}
nearlyEqual(0.1 + 0.2, 0.3); // true
The values 1e-9 and 1e-12 are placeholders for your domain, not universal constants. Choose them from the precision of your inputs and the error your calculations accumulate.
4. Use decimal arithmetic when decimal rules are required
A decimal arithmetic library represents decimal numbers with explicit scale and rounding rules, so calculations behave the way a person writing the numbers by hand would expect. This is the right choice when a calculation must follow specific rounding modes or when the scale is not fixed in advance, such as in some financial or tax engines.
The trade-off is an additional dependency and its own API. This article does not recommend a particular package, and you should evaluate any library’s maintenance status, licence, and rounding configuration against your own requirements before adopting it.
Free tools Windows power users keep installed
One-click scans. No signup required.
Choosing between the four fixes
The table compares the remedies by what each one changes. Use it to match the fix to the job rather than applying all four at once.
| Remedy | What it fixes | Preserves decimal rules during calculation | Rounding control | Localized display |
|---|---|---|---|---|
toFixed / Intl.NumberFormat |
Presentation only | No | toFixed rounds the binary value; Intl.NumberFormat offers rounding options |
Intl.NumberFormat: yes; toFixed: no |
Scaled integers (plus BigInt for large ranges) |
Fixed-scale quantities such as cents | Yes, if every operation keeps the same scale | Defined by your code (for example, Math.round on integer units) |
Only at the display boundary |
| Tolerance comparison | Approximate equality | Not applicable; it compares, it does not change arithmetic | Not applicable | Not applicable |
| Decimal arithmetic library | Decimal-domain calculation with explicit rules | Yes | Set by the library’s configuration; not assessed in this article | Depends on the library; not assessed in this article |
Common mistakes
- Treating
toFixedas a correction. It changes the string you show, not the stored value, so later comparisons still fail. - Using
Number.EPSILONeverywhere. It is a reference for values near 1 only. Large magnitudes need a relative tolerance. - Mixing
BigIntandNumber. Operators such as+throw aTypeErrorwhen the operands are of different types, so convert explicitly. - Dividing
BigIntvalues and expecting fractions. The result is truncated, so7n / 2nis3n.
Start by deciding whether the value is displayed, compared, or stored as money. Then apply the matching remedy above, and test the boundary cases your application actually produces.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




