¿Por qué los infinitos de coma flotante, a diferencia de los NaNs, son iguales?


¿Por qué la comparación del infinito no sigue la lógica aplicada a las NaNs? Este código imprime false tres veces:

double a = Double.NaN;
double b = Double.NaN;
System.out.println(a == b); // false
System.out.println(a < b); //  false
System.out.println(a > b); //  false

Sin embargo, si cambio Double.NaN a Double.POSITIVE_INFINITY, obtengo true para la igualdad, pero false para las comparaciones de mayor y menor que:

double a = Double.POSITIVE_INFINITY;
double b = Double.POSITIVE_INFINITY;
System.out.println(a == b); // true
System.out.println(a < b); //  false
System.out.println(a > b); //  false

Esto parece peligroso. Suponiendo que los valores infinitos resultan de desbordamientos, imagino que es más probable que dos variables que terminaron como infinitos no serían iguales en aritmética perfecta.

Author: Cole Johnson, 2015-02-18

8 answers

Su razonamiento es que Double.POSITIVE_INFINITY no debe ser igual a sí mismo porque es "probable" que se haya obtenido como resultado de una pérdida de precisión.

Esta línea de razonamiento se aplica a todo el punto flotante. Cualquier valor finito se puede obtener como resultado de una operación inexacta. Eso no empujó al comité de estandarización IEEE 754 a definir == como siempre evaluando a falso para valores finitos, entonces, ¿por qué los infinitos deberían ser diferentes?

Como se define, == es útil para las personas que entienden lo que hace (es decir, prueba los valores de coma flotante que se han obtenido, y ciertamente no los valores que deberían haberse obtenido con cálculos reales). Para cualquiera que entienda eso, y necesita entenderlo para usar el punto flotante incluso para cálculos que no involucran infinito, tener Double.POSITIVE_INFINITY == Double.POSITIVE_INFINITY evaluar a verdadero es conveniente, aunque solo sea para probar si el resultado de punto flotante de un cálculo de punto flotante es Double.POSITIVE_INFINITY.

Que deja la pregunta de por qué NaN puede permitirse tener un comportamiento especial, y los infinitos deben seguir los mismos principios generales que los valores finitos. NaN es diferente de los infinitos: el principio subyacente del estándar IEEE 754 es que los valores son exactamente lo que son, pero el resultado de una operación se puede aproximar con respecto al resultado real, y en este caso, el valor de coma flotante resultante se obtiene de acuerdo con el modo de redondeo.

Olvida por un instante que 1.0 / 0.0 es definido como + inf, que es una molestia en esta discusión. Piense por el momento de Double.POSITIVE_INFINITY solo como el resultado de operaciones como 1.0e100 / 1.0e-300 o Double.MAX_VALUE + Double.MAX_VALUE. Para estas operaciones, + inf es la aproximación más cercana del resultado real, al igual que para las operaciones que producen un resultado finito. Por el contrario, NaN es el resultado que se obtiene cuando la operación no tiene sentido. Es defendible que NaN se comporte especialmente, pero inf es solo una aproximación de todos los valores demasiado grandes para representarlos.

En la realidad, 1.0 / 0.0también produce +inf, pero que debe considerarse una excepción. Habría sido tan coherente definir el resultado de esa operación como NaN, pero definirlo como + inf era más conveniente en la implementación de algunos algoritmos. Se proporciona un ejemplo en la página 10 en Las notas de Kahan . Más detalles de los que la mayoría deseará están en el artículo "Cortes de Rama para Funciones Elementales Complejas, o Mucho Ruido y Pocas Nueces". Yo también interpretaría la existencia en IEEE 754 de una bandera" división por cero " separada de la bandera NaN como reconocimiento de que el usuario puede querer tratar la división por cero especialmente aunque no se define como producir NaN.

 70
Author: Pascal Cuoq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-26 02:08:58

Porque ese es el estándar. El infinito representa un número mayor o menor que el Doble.MAX_VALUE/-Doble.MAX_VALUE.

NaN representa el resultado de una operación que no tenía sentido. Es decir, la operación posiblemente no salió con un número.

Supongo que la lógica es una vez que un número se hace lo suficientemente grande (infinito) y debido a la limitación de los números de coma flotante, agregar números a él no cambiará el resultado, por lo que es 'como' infinito.

Así que si si quieres comparar con números realmente grandes, en algún momento podrías decir que esos dos números grandes están lo suficientemente cerca para todos los propósitos. Pero si quieres comparar dos cosas que no son números, no puedes compararlas así que es falso. Al menos no podrías compararlos como primitivos.

 6
Author: Carlos Bribiescas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-18 13:35:16

¿Por qué los infinitos son iguales? Porque funciona.

La aritmética de coma flotante está diseñada para producir cálculos (relativamente) rápidos que preservan los errores. La idea es que no compruebes los desbordamientos u otras tonterías durante un largo cálculo; esperas hasta que esté terminado. Es por eso que las NAN se propagan de la manera en que lo hacen: una vez que tienes una NaN, hay muy pocas cosas que puedes hacer para que desaparezca. Una vez finalizado el cómputo puede buscar NANS para comprobar si algo salió mal.

Lo mismo para los infinitos: si hay una posibilidad de desbordamiento, no hagas cosas que arrojen infinitos.

Si desea ir lento y seguro, IEEE-754 tiene mecanismos para instalar manejadores de trampa para proporcionar devoluciones de llamada en su código cuando el resultado de un cálculo sería una NaN o un infinito. En su mayoría eso no se usa; por lo general es demasiado lento y sin sentido una vez que el código se ha depurado correctamente( no es que eso sea fácil: la gente obtiene doctorados en cómo hacer esto cosas bien).

 5
Author: Pete Becker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-18 16:03:08

Otra perspectiva que justifica que los valores "infinitos" sean iguales es evitar el concepto de cardinalidad por completo. Esencialmente, si no se puede especular sobre "cuán infinito es un valor comparado con otro, dado que ambos son infinitos", es más simple asumir que Inf = Inf.

Edit: como aclaración sobre mi comentario con respecto a la cardinalidad, daré dos ejemplos con respecto a la comparación (o igualdad) de cantidades infinitas.

Considere el conjunto de positivos enteros S1 = {1,2,3, ...}, que es infinito. También considere el conjunto de enteros pares S2 = {2,4,6, ...}, que también son infinitos. Si bien hay claramente el doble de elementos en S1 que en S2, tienen "igualmente muchos" elementos ya que puede tener fácilmente una función uno-a-uno entre los conjuntos, i. e.1 -> 2, 2-> 4, ... Tienen la misma cardinalidad.

Considere en su lugar el conjunto de números reales R, y el conjunto de enteros I. De nuevo ambos son conjuntos infinitos. Sin embargo, para cada entero i hay infinitamente muchos números reales entre (i, i+1). Por lo tanto, ninguna función uno a uno puede mapear los elementos de estos dos conjuntos, y por lo tanto su cardinalidad es diferente.

Línea de Fondo: la igualdad de cantidades infinitas es complicada, más fácil de evitar en idiomas imperativos :)

 4
Author: posdef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-19 12:34:59

Me parece que "porque debería comportarse igual que cero" sería una buena respuesta. El desbordamiento aritmético y el desbordamiento deben ser manejables de manera similar.

Si se desborda el valor más grande casi infinitesimalmente pequeño que se puede almacenar en un flotador, se obtiene cero, y los ceros se comparan como idénticos.

Si se desborda del valor más grande, casi infinitamente grande, que se puede almacenar en un flotador, se obtiene INF, y los INFs se comparan como idénticos.

Esto significa que el código que maneja números que están fuera del alcance en ambas direcciones no requerirá una carcasa especial separada para una u otra. En su lugar, ambos o ninguno de los dos tendrán que ser tratados de manera diferente.

Y el requisito más simple está cubierto por el caso" neither": si desea verificar si algo sobre / underflowed, puede compararlo con zero / INF utilizando solo los operadores de comparación aritmética normales, sin necesidad de conocer la sintaxis especial del lenguaje actual para el comando checking: is it Matemáticas.isInfinite (), Float.checkForPositiveInfinity (), hasOverflowed ()...?

 3
Author: Dewi Morgan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-19 00:43:55

La respuesta correcta es simple "porque el estándar (y los documentos) lo dicen". Pero no voy a ser cínica porque es obvio que eso no es lo que buscas.


Además de las otras respuestas aquí, trataré de relacionar los infinitos con la aritmética saturadora.

Otras respuestas ya han declarado que la razón por la que las comparaciones en NaNs resultan en true, así que no voy a vencer a un caballo muerto.

Digamos que tengo un entero saturador que representa colores en escala de grises. ¿Por qué estoy usando la aritmética de saturación? Porque cualquier cosa más brillanteque el blanco sigue siendo blanco, y cualquier cosa más oscuraque el negro sigue siendo negro (excepto naranja). Eso significa BLACK - x == BLACK y WHITE + x == WHITE. Tiene sentido?

Ahora, digamos que queremos representar esos colores en escala de grises con un (firmado) complemento 1s entero de 8 bits donde BLACK == -127 y WHITE == 127. Por qué 1s complementar? Porque nos da un firmado zero like IEEE 754 floating point. Y, porque estamos usando aritmética de saturación, -127 - x == -127 y 127 + x == 127.

¿Cómo se relaciona esto con los infinitos de coma flotante? Reemplazar el entero con punto flotante, BLACK con NEGATIVE_INFINITY, y WHITE con POSITIVE_INFINITY y ¿qué se obtiene? NEGATIVE_INFINITY - x == NEGATIVE_INFINITY y POSITIVE_INFINITY + x == POSITIVE_INFINITY.

Ya que usaste POSITIVE_INFINITY, lo usaré también. Primero necesitamos una clase para representar nuestro color saturado basado en enteros; llamémoslo SaturatedColor y asumamos que funciona como cualquier otro entero en Java. Ahora, vamos a tomar su código y reemplazar double con nuestro propio SaturatedColor y Double.POSITIVE_INFINITY con SaturatedColor.WHITE:

SaturatedColor a = SaturatedColor.WHITE;
SaturatedColor b = SaturatedColor.WHITE;

Como establecimos anteriormente, SaturatedColor.WHITE (solo WHITE arriba) es 127, así que hagamos esto aquí:

SaturatedColor a = 127;
SaturatedColor b = 127;

Ahora tomamos las declaraciones System.out.println que usó y reemplazamos a y b con su valor (valores?):

System.out.println(127 == 127);
System.out.println(127 < 127);
System.out.println(127 > 127);

Debería ser obvio lo que esto imprimirá.

 2
Author: Cole Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-18 16:58:26

Desde el Doble.Nan.igual (Doble.NaN) se mencionó: Es una cosa lo que debe suceder cuando se realiza aritmética y comparar números, es una cosa totalmente diferente cuando se considera cómo los objetos deben comportarse.

Dos casos típicos de problemas son: Ordenar una matriz de números y usar valores hash para implementar diccionarios, conjuntos, etc. Hay dos casos excepcionales donde el orden normal con no se aplica: Un caso es que +0 = -0 y el otro es que NaN ≠ NaN, y x NaN, x = NaN siempre será falso lo que x es.

Los algoritmos de ordenación pueden tener problemas con esto. Un algoritmo de ordenación puede asumir que x = x es siempre verdadero. Así que si sé que x se almacena en una matriz y buscarlo, yo no podría hacer ninguna verificación de límites porque la búsqueda de ella debe encontrar algo. No si x es NaN. Un algoritmo de ordenación puede asumir que exactamente uno de a = b debe ser verdadero. No si uno es NaN. Así que un algoritmo de clasificación ingenuo puede fallar cuando NaNs están presentes. Tendrías que decidir dónde quieres que terminen las NAN al ordenar la matriz, y luego cambiar tu código de comparación para que funcione.

Ahora diccionarios y conjuntos y generalmente hashing: ¿Qué pasa si uso una NaN como clave? Un conjunto contiene objetos únicos. Si el conjunto contiene una NaN y trato de agregar otra, ¿es única porque no es igual a la que ya está ahí? ¿Qué pasa con +0 y -0, deben considerarse iguales o diferentes? Hay la regla de que cualquier dos los elementos considerados iguales deben tener el mismo valor hash. Así que lo sensato es (probablemente) que una función hash devuelve un valor único para todos los NAN, y un valor único para +0 y -0. Y después de la búsqueda de hash cuando necesite encontrar un elemento con el mismo valor de hash que sea realmente igual, dos NAN deben considerarse iguales (pero diferentes de cualquier otra cosa).

Probablemente por eso Doble.Nan.equal () se comporta diferente de ==.

 2
Author: gnasher729,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-18 17:45:08

Esto se debe a que NaN no es un número y, por lo tanto, no es igual a ningún número incluyendo NaN.

 1
Author: JavaFanatic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-18 13:26:51