É seguro verificar valores de ponto flutuante para igualdade para 0?

Eu sei que você não pode confiar na igualdade entre valores de tipo double ou decimal normalmente, mas eu estou querendo saber se 0 é um caso especial.

Embora eu possa entender imprecisões entre 0,00000000000001 e 0,00000000000002, 0 em si parece ser muito difícil de ser confundido, já que não é nada. Se você é impreciso em nada, não é nada mais.

Mas eu não sei muito sobre esse assunto, então não é para eu dizer.

double x = 0.0; return (x == 0.0) ? true : false; 

Isso sempre retornará verdadeiro?

É seguro esperar que a comparação retorne true se e somente se a variável double tiver um valor exatamente igual a 0.0 (o que, no seu trecho de código original, é claro, é o caso). Isso é consistente com a semântica do operador == . a == b significa ” a é igual a b “.

Não é seguro (porque não é correto ) esperar que o resultado de algum cálculo seja zero em aritmética dupla (ou, mais geralmente, de ponto flutuante) sempre que o resultado do mesmo cálculo em Matemática pura for zero. Isso ocorre porque, quando os cálculos chegam ao solo, aparece um erro de precisão de ponto flutuante – um conceito que não existe na aritmética do número real em matemática.

Se você precisar fazer muitas comparações de “igualdade”, pode ser uma boa idéia escrever uma pequena function auxiliar ou um método de extensão no .NET 3.5 para comparar:

 public static bool AlmostEquals(this double double1, double double2, double precision) { return (Math.Abs(double1 - double2) <= precision); } 

Isso poderia ser usado da seguinte maneira:

 double d1 = 10.0 * .1; bool equals = d1.AlmostEquals(0.0, 0.0000001); 

Para sua amostra simples, esse teste está bem. Mas e quanto a isso:

 bool b = ( 10.0 * .1 - 1.0 == 0.0 ); 

Lembre-se que .1 é um decimal repetitivo em binário e não pode ser representado exatamente. Em seguida, compare isso com este código:

 double d1 = 10.0 * .1; // make sure the compiler hasn't optimized the .1 issue away bool b = ( d1 - 1.0 == 0.0 ); 

Vou deixar você fazer um teste para ver os resultados reais: é mais provável que você se lembre dessa maneira.

Na input do MSDN para Double.Equals :

Precisão nas comparações

O método Equals deve ser usado com cuidado, porque dois valores aparentemente equivalentes podem ser desiguais devido à diferença de precisão dos dois valores. O exemplo a seguir relata que o valor Double .3333 e o Double retornado dividindo 1 por 3 são desiguais.

Em vez de comparar a igualdade, uma técnica recomendada envolve definir uma margem de diferença aceitável entre dois valores (como 0,01% de um dos valores). Se o valor absoluto da diferença entre os dois valores for menor ou igual a essa margem, a diferença provavelmente será devida a diferenças na precisão e, portanto, os valores provavelmente serão iguais. O exemplo a seguir usa essa técnica para comparar .33333 e 1/3, os dois valores Double que o exemplo de código anterior encontrou como desiguais.

Além disso, consulte Double.Epsilon .

O problema surge quando você está comparando diferentes tipos de implementação de valor de ponto flutuante, por exemplo, comparando float com double. Mas com o mesmo tipo, não deve ser um problema.

 float f = 0.1F; bool b1 = (f == 0.1); //returns false bool b2 = (f == 0.1F); //returns true 

O problema é que o programador às vezes esquece que o tipo de conversão implícito (double to float) está acontecendo para a comparação e resulta em um bug.

Se o número foi atribuído diretamente ao float ou double, então é seguro testar contra zero ou qualquer número inteiro que possa ser representado em 53 bits para um double ou 24 bits para um float.

Ou, para colocar de outra forma, você sempre pode atribuir e inteirar valor a um double e então comparar o double de volta ao mesmo inteiro e ter a garantia de que será igual.

Você também pode começar atribuindo um número inteiro e fazer comparações simples, continuando a trabalhar adicionando, subtraindo ou multiplicando por números inteiros (supondo que o resultado seja menor que 24 bits para um float e 53 bits para um double). Então você pode tratar floats e duplas como números inteiros sob certas condições controladas.

Não, não está bem. Os chamados valores desnormalizados (subnormal), quando comparados iguais a 0,0, seriam comparados como falsos (não-zero), mas quando usados ​​em uma equação seriam normalizados (se tornam 0.0). Assim, usar isso como um mecanismo para evitar uma divisão por zero não é seguro. Em vez disso, adicione 1.0 e compare com 1.0. Isso garantirá que todos os subnormais sejam tratados como zero.

Tente isso e você verá que == não é confiável para double / float.
double d = 0.1 + 0.2; bool b = d == 0.3;

Aqui está a resposta do Quora.

Na verdade, acho que é melhor usar os códigos a seguir para comparar um valor duplo com o valor 0,0:

 double x = 0.0; return (Math.Abs(x) < double.Epsilon) ? true : false; 

O mesmo para float:

 float x = 0.0f; return (Math.Abs(x) < float.Epsilon) ? true : false;