1. Introdução
Parece que se o computador é "esperto", então 0.1 + 0.2 deveria ser só 0.3. Mas não é bem assim não. Bora ver isso num exemplo simples.
double x = 0.1;
double y = 0.2;
double sum = x + y;
Console.WriteLine(sum); // O que o programa vai mostrar?
Agora tenta comparar com 0.3:
Console.WriteLine(sum == 0.3); // Aqui vai ser true ou false?
Se tu ver False, não se assusta não!
O motivo tá na forma como os números são guardados na memória
Computador trabalha com números no sistema binário. Só que nem toda fração decimal dá pra representar certinho em binário, igual 1/3 não dá pra escrever exato em decimal (0.333...). Por exemplo, 0.1 em binário é uma fração infinita, então tem que "arredondar" pra guardar.
Falando de um jeito mais de boa, o double às vezes "finge" que tá guardando teu número certinho, mas na real ele só guarda um valor bem próximo.
2. Que tipo de "bug" rola com aritmética de double?
Bora ver uns exemplos práticos.
Exemplo 1. A clássica "mágica" do 0.1 + 0.2
double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum); // 0.30000000000000004
Console.WriteLine(sum == 0.3); // False
O computador mostrou não 0.3, mas 0.30000000000000004. A diferença é pequena, mas se tu mexe com grana, por exemplo, isso já é tenso.
Exemplo 2. Soma iterativa
double result = 0;
for (int i = 0; i < 10; i++)
{
result += 0.1;
}
Console.WriteLine(result); // 0.9999999999999999
Tu queria 1.0 — mas veio um pouquinho menos. De novo, culpa do arredondamento dentro do double.
Por que isso importa na vida real
Muita gente pensa: "Ah, que diferença faz, é só um errinho, deixa pra lá!" Bora ver um exemplo do mundo dos pagamentos.
Imagina que teu internet banking soma 100 transações de 0.1 euro cada. Se teu programa "perde" uma centésima de milésimo de euro em cada vez, no banco inteiro tu já "perdeu" dinheiro de verdade. Aí o contador já chega em você e pergunta: "Cadê nosso dinheiro?!"
3. Como comparar números de ponto flutuante do jeito certo
Como o double quase nunca guarda exatamente o valor que tu espera, comparar direto com == pode te deixar na mão. O certo é comparar o módulo da diferença dos números com um número bem pequeno (epsilon).
Exemplo de comparação com tolerância
double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;
if (Math.Abs(a - b) < epsilon)
{
Console.WriteLine("Quase igual!"); // Assim é mais seguro comparar
}
Aqui a gente fala: "Se a diferença entre os números for menor que um milionésimo, vamos considerar que são iguais".
4. Valores especiais do double: Infinity, NaN, -Infinity
O tipo double guarda não só números, mas também uns valores especiais. Eles aparecem em situações que qualquer professor de matemática ia proibir na hora.
Infinito (Infinity)
O que acontece se tu dividir 1 por 0?
double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity
No C# (e em várias linguagens) dividir por 0 com double não dá erro! O resultado vira um valor especial chamado "infinito positivo".
Menos infinito (-Infinity)
Se tu dividir um número negativo por 0, vai dar infinito negativo:
double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity
"Não é número" (NaN — Not a Number)
Se tu fizer uma coisa bem doida, tipo tentar calcular a raiz quadrada de um número negativo:
double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN
Ou o resultado de dividir 0.0 / 0.0:
double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN
NaN — é tipo "qualquer coisa que não seria número na vida real".
Como checar valores especiais
No C# tem funções pra checar esses valores especiais:
Console.WriteLine(double.IsInfinity(result)); // true se for infinito
Console.WriteLine(double.IsNaN(result)); // true se for NaN
Tabela: Como o double reage a operações estranhas
| Operação | Resultado | O que fica guardado no double |
|---|---|---|
|
Infinity | +∞ |
|
-Infinity | -∞ |
|
NaN | Não é número |
|
NaN | Não é número |
GO TO FULL VERSION