Skip to content
float-equality · Lint · warning

Comparing floats with `==` or `!=`

Floating-point equality is imprecise. Use `~=` (adaptive tolerance), `!~=`, or `math.approx_eq_abs/_rel(...)`. For exact comparisons use the `decimal` type.

Why this fires

Floating-point numbers (float, f32, f64) are stored in IEEE-754 binary. Most decimal fractions — including familiar ones like 0.1 and 0.2 — have no exact binary representation, so the value the CPU actually stores is close to what you wrote, not equal to it.

fn main() {
    let a = 0.1 + 0.2
    let b = 0.3
    print(a == b)   // false — `a` is 0.30000000000000004
}

Comparing two floats with == or != therefore almost always asks the wrong question. The lint fires whenever at least one side of an == / != is a floating-point literal.

What to use instead

Zolo provides four idiomatic alternatives. Pick the one that matches your real question.

~= and !~= — approximate equality (adaptive tolerance)

The ~= operator combines an absolute and relative tolerance so it handles both tiny-magnitude and large-magnitude values without hand-tuning:

let a = 0.1 + 0.2
let b = 0.3
print(a ~= b)    // true
print(a !~= b)   // false

Use this for the 90% case where "are these numbers basically the same?" is what you mean.

math.approx_eq_abs(a, b, tol) — explicit absolute tolerance

When you have a known precision budget (e.g. "within 1 mm"):

math.approx_eq_abs(measured, expected, 0.001)

math.approx_eq_rel(a, b, rtol) — explicit relative tolerance

When precision should scale with magnitude (e.g. "within 0.1% of the expected value"):

math.approx_eq_rel(measured, expected, 0.001)

math.is_nan(x) — NaN check

x == x is false for NaN — that's IEEE-754, not a bug. Use the dedicated helper:

if math.is_nan(value) {
    return Err("value is NaN")
}

When you really want exact equality

For values that must compare exactly — money, identifiers, things that survive serialization — don't use a float at all. Use the decimal (or bigdecimal) type:

let price: decimal = 19.99d
let total: decimal = price * 3
print(total == 59.97d)   // true — exact

See /docs/float-precision for the full guide on decimal types and float-comparison facilities.

Suppressing the lint

If you have audited a specific comparison and know == is correct (e.g. comparing against a sentinel like 0.0 produced by your own code), opt out for that expression only:

@diagnostic(off, "float-equality")
fn is_zero_sentinel(x: float) -> bool {
    return x == 0.0
}

Or, file-wide, via the lint config. Prefer the narrow form — the broader you silence the rule, the more likely real bugs slip through.

See also

enespt-br