Saltar al contenido
float-equality · Lint · warning

Comparando floats con `==` o `!=`

La igualdad entre floats es imprecisa. Usa `~=` (tolerancia adaptativa), `!~=` o `math.approx_eq_abs/_rel(...)`. Para comparaciones exactas, usa el tipo `decimal`.

Por qué se dispara este lint

Los números de coma flotante (float, f32, f64) se almacenan en binario IEEE-754. La mayoría de las fracciones decimales — incluso valores familiares como 0.1 y 0.2 — no tienen representación binaria exacta, así que el valor que realmente almacena la CPU es cercano a lo que escribiste, no igual.

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

Por eso, comparar dos floats con == o != casi siempre es la pregunta equivocada. El lint se dispara siempre que al menos un lado de un == / != es un literal de coma flotante.

Qué usar en su lugar

Zolo ofrece cuatro alternativas idiomáticas. Elige la que coincida con la pregunta que realmente quieres hacer.

~= y !~= — igualdad aproximada (tolerancia adaptativa)

El operador ~= combina tolerancia absoluta y relativa, gestionando bien tanto valores muy pequeños como muy grandes sin necesidad de ajuste manual:

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

Usa esta para el caso común (90% de las veces) donde la pregunta es "¿son estos números básicamente iguales?".

math.approx_eq_abs(a, b, tol) — tolerancia absoluta explícita

Cuando tienes un presupuesto de precisión conocido (ej.: "dentro de 1 mm"):

math.approx_eq_abs(medido, esperado, 0.001)

math.approx_eq_rel(a, b, rtol) — tolerancia relativa explícita

Cuando la precisión debe escalar con la magnitud (ej.: "dentro del 0,1% del valor esperado"):

math.approx_eq_rel(medido, esperado, 0.001)

math.is_nan(x) — comprobación de NaN

x == x es false para NaN — eso es IEEE-754, no un bug. Usa el helper dedicado:

if math.is_nan(valor) {
    return Err("valor es NaN")
}

Cuando realmente quieres igualdad exacta

Para valores que deben compararse exactamente — dinero, identificadores, cualquier cosa que sobrevive a serialización — no uses float en absoluto. Usa el tipo decimal (o bigdecimal):

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

Consulta /docs/float-precision para la guía completa sobre tipos decimales y herramientas de comparación de floats.

Suprimiendo el lint

Si auditaste una comparación específica y sabes que == es correcto (ej.: comparando contra un centinela como 0.0 producido por tu propio código), desactívalo solo para esa expresión:

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

O, a nivel de archivo, vía la configuración del linter. Prefiere la forma estrecha — cuanto más amplio el silenciamiento, mayor la probabilidad de que pasen bugs reales.

Véase también

Buscar en Zolo

9 resultados

enespt-br