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.