Erros de Ponto Flutuante e Numéricos
Floats binários não conseguem representar muitas frações decimais exatamente. Totais financeiros, limites e verificações de igualdade falham quando o código trata float como aritmética decimal exata.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
from decimal import Decimal, ROUND_HALF_UP
price = Decimal("19.99")
qty = Decimal("3")
total = (price * qty).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
# Nunca: if a == b para floats
import math
if math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
...Quando usar isto:
0.1 + 0.2 != 0.3quebra testes ou faturamento- Agregados
float64do Pandas discordam dos totais da planilha - Ordenação ou agrupamento produz off-by-one nas fronteiras
- APIs JSON retornam caudas de float ruidosas (
19.990000000002)
Exemplo de Trabalho
from decimal import Decimal, ROUND_HALF_UP, getcontext
import math
# --- surpresa clássica de float ---
assert 0.1 + 0.2 != 0.3 # True em float IEEE 754
# --- dinheiro com Decimal ---
def line_total(unit_price: str, qty: int) -> Decimal:
price = Decimal(unit_price)
return (price * qty).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
assert line_total("19.99", 3) == Decimal("59.97")
# --- comparação segura de float ---
def nearly_equal(a: float, b: float) -> bool:
return math.isclose(a, b, rel_tol=1e-9, abs_tol=1e-12)
# --- agregação ciente do pandas 2.2+ ---
import pandas as pd
df = pd.DataFrame({"amount": ["10.10", "20.20"]})
# Caminho BUG: astype(float) cedo ainda pode acumular erro binário em escala
df["amount_dec"] = df["amount"].map(lambda x: Decimal(x))
total = sum(df["amount_dec"], Decimal("0"))
assert total == Decimal("30.30")
# --- contexto para regras bancárias ---
getcontext().prec = 28
tax_rate = Decimal("0.0825")
subtotal = Decimal("100.00")
tax = (subtotal * tax_rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
assert tax == Decimal("8.25")O que isto demonstra:
- Igualdade de float não é segura;
math.isclosecompara com tolerância Decimalconstruído a partir de strings preserva a intenção decimalquantizeaplica regras de arredondamento explícitas para dinheiro- Pipelines pandas devem atrasar a conversão de float ou permanecer em Decimal até a apresentação
Mergulho Profundo
Como Funciona
floaté IEEE 754 binary64; muitos racionais decimais não têm representação binária exataDecimalarmazena coeficientes base-10 com precisão e modos de arredondamento ajustáveisFractionlida com números racionais exatamente quando os valores são racionais- Centavos inteiros (
int) evitam float completamente para dinheiro quando a escala é fixa
Escolha de Tipo
| Caso de uso | Tipo | Por quê |
|---|---|---|
| Dinheiro, impostos, faturas | Decimal ou centavos inteiros | regras decimais determinísticas |
| Ciência/engenharia | float + isclose | desempenho, ecossistema numpy |
| Razões como 1/3 | Fraction | racional exato |
| Features de ML | float32/float64 | APIs de tensor esperam floats |
Notas Python
# Decimal RUIM a partir de float - herda imprecisão binária
Decimal(0.1) # Decimal('0.1000000000000000055511151231257827021181583404541015625')
# BOM
Decimal("0.1")Armadilhas
- Construtor
Decimal(float)- importa imprecisão de float para Decimal. Correção:Decimal(str(x))ou parse de entrada de string. - Misturar Decimal e float em operações - levanta
TypeErrorou coagindo inesperadamente. Correção: escolher um tipo por fronteira. - Serialização JSON -
Decimalnão é nativo JSON. Correção: quantizar para string ou centavos inteiros na camada de API. - Ordem de soma float64 numpy/pandas - somas não associativas diferem por ordem. Correção: soma de Kahan, passagem Decimal, ou centavos inteiros.
- Floor/ceil em negativos -
int(-1.2)trunca em direção a zero, não para baixo. Correção:math.floor/math.ceil. - Arredondamento de exibição vs valor armazenado - UI mostra 2 decimais, backend compara float completo. Correção: quantizar na fronteira de comparação.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Unidades menores inteiras (centavos) | escala de moeda fixa | FX com muitos lugares decimais |
Decimal | regras decimais humanas | loops internos de ML de alto rendimento |
float + epsilon | gráficos/simulação | totais financeiros regulatórios |
| Polars 1.x dtype Decimal | colunas decimais ETL tipadas | scripts simples |
FAQs
Por que Python mostra 0.30000000000000004?
Essa é a representação binary64 mais próxima. print mostra a forma de round-trip mais curta; o valor não é exatamente 0.3.
APIs devem usar float ou string para dinheiro?
Prefira decimal em string ou unidades menores inteiras em JSON. Se float for inevitável, documente a tolerância e nunca use igualdade bruta.
Qual modo de arredondamento o faturamento deve usar?
Depende da regulamentação; ROUND_HALF_UP é comum para varejo. Codifique a regra em uma função e teste casos de borda (0.5 ulp).
math.isclose é suficiente para dinheiro?
Não para igualdade de dinheiro. Use quantize Decimal ou centavos inteiros. isclose serve para floats científicos.
Como as colunas Int64 anuláveis do pandas ajudam?
Armazene centavos como Int64 no pandas 2.2+ para evitar float completamente até a formatação de exibição.
O Polars lida com Decimal?
Polars 1.x suporta dtypes Decimal para ETL; converta para string/int para APIs JSON explicitamente.
E sobre numpy.isclose vs math.isclose?
Ambos existem; a versão numpy lida com arrays. Escolha um módulo por camada de codebase para consistência.
Posso usar Fraction para dinheiro?
Possível para preços racionais, mas estranho para tabelas de impostos decimais. Decimal combina melhor com regras base-10 humanas.
Por que os totais de importação CSV divergem?
Vírgulas de localidade e análise implícita de float. Leia como string, parse para Decimal, então quantize.
Como testar arredondamento?
Testes orientados por tabela em fronteiras de meio centavo, valores negativos e divisões de impostos em três partes que devem somar o total da fatura.
O PyTorch 2.6+ usa float32 por padrão?
Muitos tensores usam float32 por padrão. Não use floats torch para matemática de ledger; mantenha finanças em caminhos Decimal/int.
Quando o float binário é OK apenas para exibição de moeda?
Quando os valores já estão quantizados para centavos e você apenas formata para a UI, mas mantém o ledger em centavos inteiros de ponta a ponta.
Relacionados
- Defeitos de Pipeline de Dados - efeitos colaterais de coerção de dtype
- Erros de Codificação e Unicode - análise de strings de números de localidade
- Snippets de Refatoração de Depuração - refatorações de float para Decimal
- Modelagem de Domínio - objetos de valor de Dinheiro
- Ferramentas de Depuração - inspecionar valores em pdb
Versões da Stack: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.