¿Es seguro comprobar los valores de punto flotante para la igualdad en 0?

Sé que no puede confiar en la igualdad entre los valores de tipo de doble o decimal normalmente, pero me pregunto si 0 es un caso especial.

Si bien puedo entender las imprecisiones entre 0.00000000000001 y 0.00000000000002, 0 en sí mismo parece bastante difícil de arruinar, ya que no es nada. Si eres imprecisa en nada, ya no es nada.

Pero no sé mucho sobre este tema, así que no me corresponde a mí decirlo.

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

¿Eso siempre volverá verdadero?

Es seguro esperar que la comparación sea true si y solo si la variable doble tiene un valor de exactamente 0.0 (que en el fragmento de código original es, por supuesto, el caso). Esto es consistente con la semántica del operador == . a == b significa ” a es igual a b “.

No es seguro (porque no es correcto ) esperar que el resultado de algún cálculo sea cero en aritmética doble (o, más generalmente, coma flotante) siempre que el resultado del mismo cálculo en Matemáticas puras sea cero. Esto se debe a que cuando los cálculos llegan al suelo, aparece un error de precisión de coma flotante, un concepto que no existe en Aritmética de números reales en Matemáticas.

Si necesita hacer muchas comparaciones de “igualdad”, puede ser una buena idea escribir una pequeña función auxiliar o un método de extensión en .NET 3.5 para comparar:

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

Esto podría usarse de la siguiente manera:

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

Para su muestra simple, esa prueba está bien. Pero que hay de esto:

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

Recuerde que .1 es un decimal que se repite en binario y no se puede representar exactamente. Luego, compare eso con 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 ); 

Lo dejaré para ejecutar una prueba para ver los resultados reales: es más probable que lo recuerde de esa manera.

Desde la entrada de MSDN para Double.Equals :

Precisión en las comparaciones

El método Equals debe usarse con precaución, porque dos valores aparentemente equivalentes pueden ser desiguales debido a la diferente precisión de los dos valores. El siguiente ejemplo informa que el valor Doble .3333 y el Doble devuelto al dividir 1 por 3 son desiguales.

En lugar de comparar por igualdad, una técnica recomendada implica definir un margen aceptable de diferencia entre dos valores (como el 0,01% de uno de los valores). Si el valor absoluto de la diferencia entre los dos valores es menor o igual a ese margen, es probable que la diferencia se deba a diferencias en la precisión y, por lo tanto, es probable que los valores sean iguales. El siguiente ejemplo usa esta técnica para comparar .33333 y 1/3, los dos valores dobles que el ejemplo de código anterior encontró que eran desiguales.

Además, vea Double.Epsilon .

El problema surge cuando se comparan diferentes tipos de implementación de valores en coma flotante, por ejemplo, comparando float con double. Pero con el mismo tipo, no debería ser un problema.

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

El problema es que, en ocasiones, el progtwigdor olvida que está ocurriendo un molde de tipo implícito (doble para flotar) para la comparación y el resultado es un error.

Si el número se asignó directamente al flotador o al doble entonces es seguro probar contra cero o cualquier número entero que pueda representarse en 53 bits para un doble o 24 bits para un flotador.

O para decirlo de otra manera, siempre puede asignar un valor entero a un doble y luego comparar el doble con el mismo número entero y garantizar que será igual.

También puede comenzar asignando un número entero y hacer que las comparaciones simples continúen funcionando al limitarse a sumr, restar o multiplicar por números enteros (suponiendo que el resultado es menos de 24 bits para un flotante y 53 bits para un doble). De modo que puede tratar flotantes y dobles como enteros bajo ciertas condiciones controladas.

No, no está bien. Los denominados valores desnormalizados (subnormales), cuando se comparan igual a 0.0, se compararían como falsos (distintos de cero), pero cuando se usan en una ecuación se normalizarían (se convertirían en 0,0). Por lo tanto, usar esto como un mecanismo para evitar una división por cero no es seguro. En su lugar, agregue 1.0 y compare con 1.0. Esto asegurará que todos los subnormales sean tratados como cero.

Intente esto, y encontrará que == no es confiable para double / float.
double d = 0.1 + 0.2; bool b = d == 0.3;

Aquí está la respuesta de Quora.

En realidad, creo que es mejor usar los siguientes códigos para comparar un valor doble contra 0.0:

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

Lo mismo para flotar:

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