Problema Com Número Decimal - Sistema de numeração decimal - Problemas de adição e subtração com ...
Sistema de numeração decimal - Problemas de adição e subtração com ...

Quando o decimal não obedece

O problema com número decimal aparece quando você espera que 0,1 + 0,2 dê exatamente 0,3 e o sistema retorna algo como 0,30000000000000004. Isso não é um bug aleatório. É como computadores de ponto flutuante funcionam desde os anos 1980. A norma IEEE 754 define que números decimais são representados em binário, e 0,1 em binário é uma fração periódica infinita — 0,0001100110011... — então o processador corta na casa que couber no espaço disponível e você acumula um erro de arredondamento. Achei isso estranho na primeira vez que rodava uma conta de juro simples num sistema legado em Java. O resultado batia 0,30000000000000004 e o relatório financeiro truncava centavos. Eu passei duas horas caçando "erro de lógica" até perceber que não havia lógica para caçar. A solução foi mudar a representação de double para BigDecimal com escala definida, usando RoundingMode.HALF_UP. Depois disso, o mesmo cálculo ficou estável e o relatório fechou corretamente.

Como contornar o problema com número decimal

Antes de falar de workaround, é importante entender onde o problema aparece com mais frequência. Linguagens como JavaScript, Python, C, Java e até planilhas usam representação binária de ponto flutuante por padrão. Qualquer operação que envolva frações que não sejam potências de 2 gera esse comportamento. 0,1, 0,2, 0,3, 0,7 — todos são irracionais dentro da base binária. O que parece um erro de cálculo é apenas um limite da representação. O jeito mais direto de resolver depende do que você está fazendo. Se for matemática financeira, dinheiro, impostos ou qualquer coisa onde centavo importa, use tipos decimais exatos. Em Java, BigDecimal. Em C#, decimal. Em Python, decimal.Decimal. Em JavaScript, não existe tipo nativo decimal, então você recorre a bibliotecas como decimal.js ou big.js. Em Python, a função Decimal do módulo decimal permite especificar contexto de precisão e Modo de arredondamento, o que elimina a maior parte dos problemas práticos.

Se você precisa apenas exibir e não fazer cálculos pesados, uma casa de formatação resolve visualmente. Em JavaScript, (0.1 + 0.2).toFixed(2) mostra 0,30. Em Python, format(0.1 + 0.2, '.2f') faz o mesmo. Isso não corrige o valor interno, só esconde. Para relatórios e UI é suficiente, mas se você passar esse resultado arredondado para outra operação, o erro volta. Eu já vi gente usar toFixed em loop de agregação e o relatório final bater diferença de reais por acúmulo de invisibilidade. Outra saída comum é trabalhar com inteiros. Representar tudo em centavos, miligramas, segundos ou a unidade menor relevante e fazer a conta inteira. Isso funciona muito bem em sistemas embarcados, games e cálculos onde a faixa de valores é conhecida e limitada. Você divide só na hora de mostrar. O risco aqui é overflow se a escala crescer e você esquecer de ajustar o tipo de dado. Um short de 16 bits estoura fácil com valores grandes.

Se o problema é comparação, nunca use igualdade direta com ponto flutuante. Ao invés de a == b, verifique se |a - b|

epsilon. O epsilon depende da sua precisão. Para contas financeiras com dupla precisão, 1e-9 costuma ser seguro. Para gráficos ou simulações, 1e-6 ou até 1e-3 pode ser aceitável. Escolha com consciência, não copie número de Stack Overflow sem entender o contexto. Há um detalhe que muita gente perde: arredondamento e casas decimais não são a mesma coisa. Arredondar para duas casas não significa que o valor está correto. Ele continua sendo uma aproximação binária. Se você precisa garantir que o resultado final seja exatamente 0,30, o caminho é calcular comBigDecimal ou Decimal desde o início, não aplicar formatação por cima de double.

👉 Clique no botão abaixo para saber mais sobre o assunto!

Planilhas também sofrem. O Excel usa IEEE 754 por padrão. Você pode configurar precisão como exibido, mas isso muda todos os cálculos da pasta para sempre e não reverte. Eu vi uma planilha de folha de pagamento que, após ativar essa opção, começou a entregar valores diferentes em lotes de funcionários porque as somas intermediárias foram truncadas de formas distintas. A correção foi refazer a lógica com casas fixas e revisar os efeitos colaterais em abas vinculadas. Em banco de dados, o tipo DECIMAL ou NUMERIC armazena exatamente o valor que você insere. Se sua tabela usa DOUBLE ou FLOAT para preços, todas as consultas de totalização podem apresentar diferenças. Migrar para DECIMAL(19,4) resolve na maioria dos casos, mas exige revisão de stored procedures e integrações que confiavam no comportamento antigo. Eu migrei uma tabela de transações e o insert batch que antes levava 40 segundos passou a levar 2 minutos porque o driver fazia conversões implícitas diferentes. Ajustei o mapeamento ORM e caiu para 45 segundos.

O que mais gera confusão é a impressão dos valores. Alguns ambientes arredondam na tela, outros truncam, outros mostram a representação binária aproximada. Se você está depurando e o valor exibido parece certo mas a lógica falha, desconfie da camada de apresentação. Imprima com Mais casas decimais ou use inspeção de memória para ver o valor real. Um case prático que eu enfrentei foi um cálculo de desconto em cascata em um e-commerce. O cupom aplicava 15% e depois um frete fixo. O sistema usava double e, em cerca de 3% das compras, o total final saía com diferença de 0,01. A raiz era a composição de múltiplas multiplicações e somas com frações binárias. A correção foi mover todo o cálculo de preço para uma função que usava Decimal com escala 2 e rounding HALF_UP em cada etapa intermediária. O volume de pedidos aumentou e a diferença zero se manteve estável por meses.

Se você não pode mudar o tipo de dado por restrição de legado, a alternativa é usar tolerância em toda comparação e arredondamento controlado só na saída. Não misture as duas coisas no meio do caminho. Defina uma regra clara: operar com tolerância, apresentar com escala fixa, e documentar onde cada corte acontece. Sem documentar, quem chegar depois vai achar que o arredondamento é opcional e vai espalhar inconsistência. Resumindo sem resumir: o problema com número decimal existe porque binário não representa bem certos decimais. A solução não é tentar forçar o float a se comportar como decimal. É escolher a ferramenta certa para o trabalho, limitar onde o arredondamento acontece e testar com valores que exponham o erro, como 0,1 + 0,2, 19,99 * 1,15, e somas repetidas de 0,1. Se esses casos fecham certinho, seu tratamento está funcionando.