Why is this an issue?

JavaScript’s number type uses IEEE 754 double-precision floating-point arithmetic. Many decimal fractions, and division results whose exact value is non-integer cannot be represented exactly in binary, so calculations that look simple may produce slightly different values.

0.1 + 0.2 === 0.3; // false

As a result, exact equality and inequality checks on floating-point-sensitive values can produce unexpected results. For JavaScript and TypeScript, this rule is intentionally conservative: it targets exact comparisons on values that are statically floating-point-sensitive, such as decimal arithmetic or floating-point constants, not every equality check on number.

How to fix it

When the compared values are expected to be close rather than exactly identical, use a tolerance-based comparison instead of an exact equality or inequality check. When the arithmetic is around the magnitude of 1, Number.EPSILON is often a reasonable starting threshold, following the testing-equality guidance in the MDN documentation. For larger magnitudes or lower-precision inputs, a larger fixed tolerance may be needed. Choose one that fits the expected precision of the values being compared.

In application code

Noncompliant code example

const total = 0.1 + 0.2;
const average = 0.9 + 0.2;

if (total === 0.3) { // Noncompliant: exact comparison on decimal arithmetic
  publish(total);
}

const status = total === 0.3 ? "done" : "retry"; // Noncompliant: exact comparison inside a ternary

if (average <= 1.1 && average >= 1.1) { // Noncompliant: equivalent to an exact "===" test
  publish(average);
}

if (average < 1.1 || average > 1.1) { // Noncompliant: equivalent to an exact "!==" test
  publish(average);
}

if (getRatio() !== 10 / 3) { // Noncompliant: exact comparison against fraction-producing arithmetic
  publish(getRatio());
}

Compliant solution

const total = 0.1 + 0.2;
const average = 0.9 + 0.2;

if (Math.abs(total - 0.3) < Number.EPSILON) {
  publish(total);
}

const status = Math.abs(total - 0.3) < Number.EPSILON ? "done" : "retry";

if (Math.abs(average - 1.1) < Number.EPSILON) {
  publish(average);
}

if (Math.abs(average - 1.1) >= Number.EPSILON) {
  publish(average);
}

if (Math.abs(getRatio() - 10 / 3) >= 1e-12) {
  publish(getRatio());
}

In Jest, Vitest, Bun, and Playwright

In frameworks with Jest-style expect matchers, use an approximate matcher instead of an exact matcher when the expected value is floating-point-sensitive.

Noncompliant code example

import { expect, test } from "vitest";

test("computes a total", () => {
  expect(0.1 + 0.2).toBe(0.3);
});

Compliant solution

import { expect, test } from "vitest";

test("computes a total", () => {
  expect(0.1 + 0.2).toBeCloseTo(0.3);
});

In Node.js

With Node.js assert, replace exact equality assertions on floating-point-sensitive values with a tolerance-based predicate or a helper that performs an approximate comparison.

Noncompliant code example

import assert from "node:assert/strict";

assert.strictEqual(0.1 + 0.2, 0.3);

Compliant solution

import assert from "node:assert/strict";

function almostEqual(actual, expected, tolerance = Number.EPSILON) {
  return Math.abs(actual - expected) < tolerance;
}

assert.ok(almostEqual(0.1 + 0.2, 0.3));

In Chai and Cypress

With Chai, replace exact equality assertions on floating-point-sensitive values with approximate assertions such as closeTo or approximately. The same guidance applies to Cypress, which exposes Chai assertions through expect and .should().

Noncompliant code example

import { expect } from "chai";

expect(0.1 + 0.2).to.equal(0.3);

Compliant solution

import { expect } from "chai";

expect(0.1 + 0.2).to.be.closeTo(0.3, Number.EPSILON);

Resources

Documentation