Pular para o conteúdo
float-equality · Lint · warning

Comparando floats com `==` ou `!=`

A igualdade entre floats é imprecisa. Use `~=` (tolerância adaptativa), `!~=`, ou `math.approx_eq_abs/_rel(...)`. Para comparações exatas, use o tipo `decimal`.

Por que esse lint aparece

Números de ponto flutuante (float, f32, f64) são armazenados em binário IEEE-754. A maioria das frações decimais — inclusive valores familiares como 0.1 e 0.2 — não tem representação binária exata, então o valor que a CPU realmente armazena é próximo do que você escreveu, não igual.

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

Por isso, comparar dois floats com == ou != quase sempre é a pergunta errada. O lint dispara sempre que pelo menos um lado de uma == / != é um literal de ponto flutuante.

O que usar no lugar

Zolo oferece quatro alternativas idiomáticas. Escolha a que casa com a pergunta que você realmente quer fazer.

~= e !~= — igualdade aproximada (tolerância adaptativa)

O operador ~= combina tolerância absoluta e relativa, lidando bem tanto com valores muito pequenos quanto muito grandes sem precisar de ajuste manual:

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

Use este para o caso comum (90% das vezes) onde a pergunta é "esses números são basicamente iguais?".

math.approx_eq_abs(a, b, tol) — tolerância absoluta explícita

Quando você tem um orçamento de precisão conhecido (ex.: "dentro de 1 mm"):

math.approx_eq_abs(medido, esperado, 0.001)

math.approx_eq_rel(a, b, rtol) — tolerância relativa explícita

Quando a precisão deve escalar com a magnitude (ex.: "dentro de 0,1% do valor esperado"):

math.approx_eq_rel(medido, esperado, 0.001)

math.is_nan(x) — checagem de NaN

x == x é false para NaN — isso é IEEE-754, não bug. Use o helper específico:

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

Quando você realmente precisa de igualdade exata

Para valores que precisam ser comparados exatamente — dinheiro, identificadores, qualquer coisa que sobrevive a serialização — não use float de jeito nenhum. Use o tipo decimal (ou bigdecimal):

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

Veja /docs/float-precision para o guia completo sobre tipos decimais e ferramentas de comparação de floats.

Suprimindo o lint

Se você auditou uma comparação específica e tem certeza de que o == está correto (ex.: comparando contra uma sentinela como 0.0 que o seu próprio código produziu), desabilite só naquela expressão:

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

Ou, em escopo de arquivo, pelo lint config. Prefira a forma estreita — quanto mais amplo o silenciamento, maior a chance de bugs reais passarem.

Veja também

Buscar no Zolo

9 resultados

enespt-br