CodeGym /Cursos /C# SELF /Problemas de precisão e valores especiais

Problemas de precisão e valores especiais

C# SELF
Nível 6 , Lição 3
Disponível

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?
Soma de números de ponto flutuante: a gente espera 0.3, mas será que é isso mesmo?

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
1.0 / 0.0
Infinity +∞
-1.0 / 0.0
-Infinity -∞
0.0 / 0.0
NaN Não é número
Math.Sqrt(-1)
NaN Não é número
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION