JavaScript's Floating-Point Equality Trap

2026-04-25

You're building a simple shopping cart. This function checks whether a customer has paid the exact amount owed, accumulating their individual item prices and comparing against the expected total:

function verifyPayment(itemPrices, expectedTotal) {
    let computedTotal = 0;
    for (const price of itemPrices) {
        computedTotal += price;
    }

    if (computedTotal === expectedTotal) {
        return "Payment verified";
    } else {
        const diff = computedTotal - expectedTotal;
        return `Discrepancy of $${diff.toFixed(2)} detected`;
    }
}

// Customer bought three items
const prices = [0.1, 0.2, 0.3];
const total = 0.6;

console.log(verifyPayment(prices, total));
// Expected: "Payment verified"
// Actual:   "Discrepancy of $0.00 detected"

The output is baffling. It claims there's a discrepancy — but the discrepancy rounds to $0.00. The numbers 0.1 + 0.2 + 0.3 obviously equal 0.6. What's going on?

The Bug

IEEE 754 floating-point arithmetic does not represent decimal fractions exactly. The value 0.1 in binary is a repeating fraction, like 1/3 in decimal. When you add these imprecise representations together, the rounding errors accumulate unpredictably.

In this case:

0.1 + 0.2 === 0.30000000000000004  // not 0.3!
0.30000000000000004 + 0.3 === 0.6000000000000001  // not 0.6!

So computedTotal ends up as 0.6000000000000001, which is not === 0.6. The strict equality check fails. Then toFixed(2) rounds the tiny difference (1.1e-16) to "0.00", producing the maddening message that there's a discrepancy of zero dollars.

This bug is especially insidious because:

The Fix

For financial calculations, the gold standard is to work in the smallest currency unit as integers (cents, not dollars). If you must compare floats, use an epsilon tolerance:

function verifyPayment(itemPrices, expectedTotal) {
    let computedTotal = 0;
    for (const price of itemPrices) {
        computedTotal += price;
    }

    // Compare with a tolerance appropriate for currency
    const EPSILON = 0.005; // half a cent
    if (Math.abs(computedTotal - expectedTotal) < EPSILON) {
        return "Payment verified";
    } else {
        const diff = computedTotal - expectedTotal;
        return `Discrepancy of $${diff.toFixed(2)} detected`;
    }
}

Or better yet, eliminate floats entirely:

// Store prices as integers (cents)
const pricesInCents = [10, 20, 30];
const totalInCents = 60;
// Integer addition is exact — no surprises.

This isn't a JavaScript-specific problem. The same bug bites you in Python, Java, C, Go — any language using IEEE 754 doubles. Python's decimal module and Java's BigDecimal exist precisely because this class of bug has cost real companies real money. The Patriot missile failure in 1991 and the Vancouver Stock Exchange's drifting index are famous examples of floating-point accumulation errors in production systems.

Key Takeaway: Never use === to compare floating-point results from arithmetic — use an epsilon tolerance, or better yet, represent currency as integer cents to avoid the problem entirely.

All newsletters