Problemas de Precisão com Decimais no Desenvolvimento de Software
- #Java
- #Programação para Internet
- #Kotlin
- #Arquitetura de Sistemas
Quando comecei a programar, eu esperava que números se comportassem como na matemática. Se você soma 0.1 com 0.2, o resultado deveria ser 0.3.
Mas, na maioria das linguagens de programação, acontece isto:
0.1 + 0.2 = 0.30000000000000004
Isso é um comportamento engraçado a primeiro momento, afinal nos parece ilógico este resultado. É uma consequência de como os computadores representam números.
Neste artigo, vamos explorar o problema oculto da precisão decimal e quando usar floating-point, fixed-point ou decimais de precisão arbitrária como BigDecimal.
Não esqueça dos bits
Para introduzirmos esse problema, devemos lembrar que os computadores não armazenam números da forma como escrevemos. No nível de hardware, tudo é representado usando bits, que são sequências de 0s e 1s.
Cada número precisa caber em uma quantidade limitada de bits, e essa limitação é a base do comportamento dos tipos numéricos.
Um bit é a menor unidade de informação. Quando agrupamos bits, formamos contêineres de tamanho fixo:

8 bits → 1 byte
16 bits → 2 bytes
32 bits → 4 bytes
64 bits → 8 bytes
Esses contêineres não crescem dinamicamente. Isso significa que todo tipo numérico possui:
- Um intervalo finito
- Uma precisão finita
- Possível overflow
- Possíveis erros de arredondamento
Por exemplo, com 8 bits sem sinal, podemos representar apenas:
Mínimo: 00000000 → 0
Máximo: 11111111 → 255
São apenas 256 valores possíveis. Nada fora desse intervalo pode ser armazenado.
Quando lidamos com números decimais, essa limitação se torna ainda mais importante. Precisamos decidir:
- Quantos bits representam a parte inteira
- Quantos bits representam a parte fracionária
- Se a representação é fixa ou flutuante
É aqui que surgem as representações Fixed Point e Floating Point.
Representação Fixed-Point
No fixed-point, a quantidade de bits da parte inteira e fracionária é pré-definida. O ponto decimal fica implicitamente fixo em uma posição específica.
Exemplo (8 bits no total):

- 4 bits para inteiro
- 4 bits para fração
Características:
- Aritmética rápida
- Precisão previsível
- Intervalo limitado
- Risco de overflow
- Sem escala exponencial
Essa representação é comum em:
- Sistemas embarcados
- DSP (Processamento Digital de Sinais)
- Cálculos financeiros (usando inteiros escalados)
Isso é aritmética de ponto fixo implementada com inteiros escalados.
Representação Floating-Point
Floating-point resolve o problema de intervalo armazenando números usando notação científica.
Um float de 32 bits é dividido em:
- 1 bit → sinal
- 8 bits → expoente
- 23 bits → mantissa
Vantagens:
- Intervalo numérico enorme
- Escala automática
- Ideal para computação científica
Desvantagens:
- Erros de arredondamento
- Perda de precisão
- Representação decimal não exata
- Problemas de comparação (
0.1 + 0.2 != 0.3)
Diferentes Tipos Decimais e Trade-offs de Precisão
Quando entendemos que números são limitados por bits, percebemos que diferentes tipos decimais surgem como compromissos de engenharia entre:
- Intervalo
- Precisão
- Performance
- Uso de memória
As representações mais comuns são:
- Floating-point (base binária)
- Fixed-point (inteiros escalados)
- Decimais de precisão arbitrária (BigDecimal)
Floating-Point Decimals (float / double)
Tipos floating-point armazenam decimais usando notação científica binária. Eles priorizam intervalo ao invés de precisão exata.

Segundo a documentação do W3Schools:
A precisão de float é de aproximadamente 6–7 dígitos decimais, enquanto double possui cerca de 15–16 dígitos. Portanto, é mais seguro usar double na maioria dos cálculos.
Problema clássico:
double valor1 = 0.1;
double valor2 = 0.2;
System.out.println(valor1 + valor2);
// Esperado: 0.3
// Resultado: 0.30000000000000004
Output:
0.30000000000000004
Isso acontece porque 0.1 não pode ser representado exatamente em binário.
O valor é truncado para caber nos bits disponíveis, introduzindo erro de arredondamento.
O número 0.1 em decimal vira uma fração infinita em binário, assim como 1/3 em decimal:
0.1 (base 10) = 0.000110011001100110011… (base 2)
Como floating-point possui bits limitados, essa representação infinita precisa ser cortada, gerando pequenos erros.
Floating-point é ideal para:
- Cálculos científicos
- Gráficos
- Simulações físicas
- Machine learning
- Medições (onde pequenos erros são aceitáveis)
Mas não é ideal para dinheiro.
Em áreas com números extremamente grandes ou pequenos, o padrão IEEE 754 (float/double) funciona muito bem.
Por exemplo:
- Em gráficos de jogos, erros de arredondamento são imperceptíveis
- Simulações físicas toleram pequenas imprecisões
- Modelos de IA priorizam velocidade sobre precisão exata
Fixed-Point Decimals (Inteiros Escalados)
Fixed-point evita erros de floating-point armazenando valores como inteiros com uma escala implícita.
Exemplo:
123.45 → armazenado como 12345
scale = 2
Isso garante representação decimal exata, desde que o valor caiba no intervalo do inteiro.
Usado em:
- Sistemas financeiros
- POS (ponto de venda)
- Dispositivos embarcados
- Bancos de dados (DECIMAL / NUMERIC)
Mas fixed-point ainda possui limitação de bits:
int → 32 bits
long → 64 bits
Eventualmente ocorre overflow.
Isso leva à próxima solução.
Decimais de Precisão Arbitrária (BigDecimal)
BigDecimal remove limites práticos de tamanho usando precisão arbitrária.
Em vez de usar 32 ou 64 bits, ele usa:
- Inteiro de tamanho arbitrário (BigInteger)
- Escala explícita
- Aritmética decimal exata
Conceitualmente:
BigDecimal = unscaledValue × 10^-scale
Exemplo:
BigDecimal example = new BigDecimal("123.45");
Internamente:
unscaledValue = 12345
scale = 2
Diferente de floating-point:
- Não há conversão para binário
- Não há arredondamento implícito
- Aritmética decimal exata
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));
// Resultado: 0.3
Por que BigDecimal é mais preciso?
Porque ele:
- Usa base 10 em vez de base 2
- Possui número arbitrário de dígitos
- Não trunca para caber em bits fixos
- Exige regras explícitas de arredondamento
Exemplo:
BigDecimal ex = new BigDecimal("10")
.divide(new BigDecimal("3"), 10, RoundingMode.HALF_UP);
Output:
3.3333333333
Você controla a precisão manualmente.
Porém, BigDecimal tem custo:
- Mais uso de CPU
- Mais uso de memória
- Mais lento que double
Por isso, deve ser usado apenas quando precisão é essencial.
Quando usar cada representação
Use float / double quando:
- Performance importa
- Pequenos erros são aceitáveis
- Cálculos científicos ou gráficos
Use fixed-point quando:
- Precisão decimal exata é necessária
- Intervalo conhecido
- Alta performance necessária
- Valores financeiros simples
Use BigDecimal quando:
- Cálculos financeiros
- Valores monetários
- Impostos
- Alta precisão necessária
- Nenhum erro de arredondamento é aceitável
— Erick Jhone Rodrigues da Silva
Palmas, Tocantins, 8 de abril de 2026.




