¿Cuál es la razón fundamental para todas las comparaciones que regresan falsa para valores IEEE754 NaN?

StackOverflow https://stackoverflow.com/questions/1565164

Pregunta

¿Por qué las comparaciones de valores NaN comportan de manera diferente de todos los otros valores? Es decir, todas las comparaciones con los operadores ==, <=,> =, <,>, donde uno o ambos valores es NaN devuelve falso, contrario al comportamiento de todos los demás valores.

supongo que esto simplifica el cálculo numérico, de alguna manera, pero no pude encontrar una razón indique expresamente, ni siquiera en el notas de la conferencia sobre la Condición de la IEEE 754 por Kahan que discute otras decisiones de diseño en detalle.

Esta conducta desviada está causando problemas cuando se hace el procesamiento de datos simple. Por ejemplo, al ordenar una lista de registros w.r.t. algún campo de valor real en un programa C que necesita escribir código adicional para manejar NaN como el elemento máximo, de lo contrario el algoritmo de ordenación podría llegar a ser confuso.

Editar Las respuestas hasta ahora todos argumentan que no tiene sentido comparar NaNs.

Estoy de acuerdo, pero eso no quiere decir que la respuesta correcta es falsa, más bien sería un no-a-booleana (NAB), que por suerte no existe.

Así que la elección de devolver verdadero o falso para las comparaciones es en mi opinión arbitraria, y para los datos generales procesando sería ventajoso si obedecía a las leyes usuales (Reflexividad de ==, tricotomía de <, ==,>), no sea que las estructuras de datos que se basan en estas leyes se confunden.

Así que estoy pidiendo alguna ventaja concreta de romper estas leyes, no sólo el razonamiento filosófico.

Editar 2: Creo entender ahora por qué hacer NaN máxima sería una mala idea, sería estropear el cálculo de los límites superiores.

NaN! = NaN podría ser deseable evitar la detección de convergencia en un bucle como

while (x != oldX) {
    oldX = x;
    x = better_approximation(x);
}

que sin embargo debe ser escrito mejor mediante la comparación de la diferencia absoluta con un límite pequeño. Así que en mi humilde opinión este es un argumento relativamente débil para romper la reflexividad de Nan.

¿Fue útil?

Solución

Yo era un miembro del comité IEEE-754, voy a tratar de ayudar a aclarar las cosas un poco.

En primer lugar, los números de punto flotante no son números reales, y la aritmética de punto flotante no satisface los axiomas de la aritmética real. Tricotomía no es la única propiedad de la aritmética real que no se mantiene para los flotadores, ni siquiera el más importante. Por ejemplo:

  • La suma no es asociativa.
  • La ley distributiva no se sostiene.
  • números
  • de punto flotante Hay sin inversas.

Podría seguir. No es posible especificar un tipo aritmético de tamaño fijo que satisface todos de las propiedades de la aritmética real que conocemos y amamos. El comité 754 tiene que decidir a doblar o romper algunos de ellos. Esta es guiado por algunos principios muy simples:

  1. Cuando podemos, vamos a igualar el comportamiento de la aritmética real.
  2. Cuando no podemos, tratamos de que las violaciónes como predecible y tan fácil de diagnosticar como sea posible.

En cuanto a su comentario "eso no quiere decir que la respuesta correcta es falsa", esto es incorrecto. El (y < x) predicado pregunta si y es menor que x. Si y es NaN, entonces es no menor que cualquier valor x de punto flotante, por lo que la respuesta es necesariamente falsa.

he mencionado que tricotomía no se cumple para valores de coma flotante. Sin embargo, hay una propiedad similar que nos depara. Cláusula 5.11, párrafo 2 de la norma 754-2008:

  

Cuatro relaciones mutuamente exclusivos son posibles: menor que, igual, mayor que, y no ordenada. El último caso se presenta cuando al menos un operando es NaN. Cada NaN comparará no ordenada con todo, incluso a sí mismo.

En cuanto a la escritura de código extra para manejar NaNs va, por lo general es posible (aunque no siempre es fácil) para estructurar el código de una manera tal que NaNs caen a través correctamente, pero esto no es siempre el caso. Cuando no es así, algo de código adicional puede ser necesaria, pero eso es un pequeño precio a pagar por la comodidad que clausura algebraica llevó a aritmética de punto flotante.


Adición: Muchos comentaristas han argumentado que sería más útil para preservar la reflexividad de la igualdad y la tricotomía en razón de que! = No parece NaN NaN adoptar para preservar cualquier axioma familiar. Confieso a tener cierta simpatía por este punto de vista, así que pensé que volvería a visitar esta respuesta y proporcionar un poco más de contexto.

Mi comprensión de hablar con Kahan es que NaN = NaN se originó a partir de dos consideraciones prácticas:

  • Eso x == y debe ser equivalente a x - y == 0 siempre que sea posible (más allá de ser un teorema de la aritmética real, esto hace que la implementación de hardware de comparación más eficiente con el espacio, que era de suma importancia en el momento en que se desarrolló la norma - nota, sin embargo, que este es violada para x = y = infinito, así que no es una gran razón por sí sola;. que podría haber sido doblada razonablemente a (x - y == 0) or (x and y are both NaN))

  • Más importante aún, no había predicado isnan( ) en el momento en que NaN fue formalizado en el 8087 la aritmética; que era necesario proporcionar a los programadores con un medio conveniente y eficaz para la detección de valores NaN que no dependen de los lenguajes de programación que ofrecen algo así como isnan( ) que podría tomar muchos años. Voy a citar la propia escritura de Kahan sobre el tema:

  

¿No había manera de deshacerse de NaNs, que serían tan inútiles como indefinidos en Crays; tan pronto como uno fueron encontrados, el cálculo sería mejor parado en vez de continuar por tiempo indefinido a una conclusión indefinida. Es por eso que algunas operaciones sobre NaNs deben ofrecer resultados no-nan. Cuáles son las operaciones? ... Las excepciones son predicados C “x == x” y “x! = X”, que son, respectivamente, 1 y 0 para correonúmero muy finito o infinito x pero revertir si x no es un número (NaN); éstas proporcionan la única distinción simple entre unexceptional NaNs y números en las lenguas que carecen de una palabra por NaN y un predicado isNaN (x).

Tenga en cuenta que esto también es la lógica que descarta devolver algo así como un “No-A-booleana”. Tal vez este pragmatismo estaba fuera de lugar, y la norma debería haber exigido isnan( ), pero que habría hecho NaN casi imposible de utilizar de manera eficiente y cómoda durante varios años mientras el mundo esperaba que la adopción lenguaje de programación. No estoy convencido de que habría sido un compromiso razonable.

Para ser franco: el resultado de NaN == NaN no va a cambiar ahora. Es mejor aprender a vivir con ella que se quejan en el Internet. Si desea argumentar que una relación de orden adecuado para contenedores deben también existen, yo recomendaría defendiendo que su lenguaje de programación favorito implementar el predicado totalOrder estandarizado en la norma IEEE-754 (2008). El hecho de que no tiene ya habla de la validez de la preocupación de Kahan que motivó el estado actual de las cosas.

Otros consejos

NaN puede ser pensado como un estado / número indefinido. similar al concepto de 0/0 está definido o la raíz cuadrada (-3) (en el sistema de números reales en las vidas de punto flotante).

NaN se utiliza como una especie de marcador de posición para este estado indefinido. Matemáticamente hablando, no definido no es igual a indefinido. Tampoco se puede decir un valor indefinido es mayor o menor que otro valor indefinido. Por lo tanto, todas las comparaciones devuelven false.

Este comportamiento también es ventajoso en los casos donde puede comparar sqrt (-3) a sqrt (-2). Tendrían tanto NaN pero no son equivalentes a pesar de que devuelven el mismo valor. Por tanto, tener la igualdad siempre volviendo falsa cuando se trata de NaN es el comportamiento deseado.

Para lanzar en otra analogía. Si te lo entrego dos cajas, y te digo que ninguno de ellos contiene una manzana, ¿me dirá que las cajas contienen la misma cosa?

NaN no contiene ninguna información sobre lo que algo es, precisamente lo que no lo es. Por lo tanto, estos elementos pueden no duda puede decir que son iguales.

Desde el artículo de Wikipedia sobre NaN , las siguientes prácticas pueden causar NaNs:

  • Todas las operaciones matemáticas> con un NaN como al menos un operando
  • Las divisiones 0/0, ∞ / ∞, ∞ / -∞, -∞ / ∞ y -∞ / -∞
  • Las multiplicaciones 0 × 0 × ∞ y -∞
  • Las adiciones ∞ + (-∞), (-∞) + ∞ y equivalentes restas.
  • aplicación de una función a los argumentos fuera de su dominio, incluyendo tomando la raíz cuadrada de un número negativo, tomar el logaritmo de un número negativo, teniendo la tangente de un múltiplo impar de 90 grados (o pi / 2 radianes), o tomar el seno inverso o coseno de un número que es menor que -1 o mayor que 1.

Dado que no hay manera de saber cuál de estas operaciones creó el NaN, no hay manera de compararlos que tenga sentido.

No sé la razón fundamental del diseño, pero aquí es un extracto de la norma IEEE 754-1985:

.

"Será posible comparar los números de punto flotante en todos los formatos soportados, incluso si los formatos de los operandos difieren Las comparaciones son exactos y nunca desbordan ni subutilizadas son posibles cuatro relaciones mutuamente exclusivos:. Menor, igual, mayor que y desordenada. El último caso se presenta cuando al menos un operando es NaN. Cada NaN comparará no ordenada con todo, incluso a sí mismo ".

Sólo parece peculiar porque la mayoría de los entornos de programación que permiten NaNs no permiten también la lógica de 3 valorada. Si se lanza 3-valorada lógica en la mezcla, se hace constante:

  • (2,7 == 2.7) = true
  • (2,7 == 2.6) = false
  • (2,7 == NaN) = desconocido
  • (NaN == NaN) = desconocido

A pesar de .NET no proporciona un operador de bool? operator==(double v1, double v2), por lo que aún está atascado con el resultado (NaN == NaN) = false tonta.

supongo que NaN (no un número) significa exactamente eso:. Esto no es un número y la comparación de lo que no tiene mucho sentido

Es un poco como la aritmética en SQL con operandos null: Ellos todo el resultado en null

.

Las comparaciones de números de punto flotante se comparan los valores numéricos. Por lo tanto, no pueden ser utilizados para valores no numéricos. NaN por lo tanto, no puede ser comparado en un sentido numérico.

La respuesta demasiado simplista es que un NaN no tiene ningún valor numérico, por lo que no hay nada en él para comparar con cualquier otra cosa.

Se pueden realizarse las pruebas y para la sustitución de las NaNs con + INF si quiere que actúen como + INF.

NaN es una nueva instancia implícita (de un tipo especial de error de tiempo de ejecución). Eso significa NaN !== NaN por la misma razón que new Error !== new Error;

Y tener en cuenta tales implícito también se ve fuera de errores, por ejemplo, en el contexto de las expresiones habituales que significa que es el azúcar /a/ !== /a/ simplemente sintaxis para new RegExp('a') !== new RegExp('a')

Aunque estoy de acuerdo que las comparaciones de NaN con cualquier número real debe ser desordenada, me parece que una causa justa para comparar NaN consigo mismo. ¿Cómo, por ejemplo, descubre uno la diferencia entre NaNs de señalización y NaNs tranquilas? Si pensamos de las señales como un conjunto de valores booleanos (es decir, un vector de bits) uno bien podría preguntar si el vectores de bits son los mismos o diferentes y ordenar los conjuntos en consecuencia. Por ejemplo, en la decodificación de un máximo exponente sesgado, si la mantisa se quedaron desplazadas a fin de alinear el bit más significativo de la mantisa en el bit más significativo del formato binario, un valor negativo sería una tranquila NaN y cualquier valor positivo sería ser un NaN de señalización. Cero, por supuesto, está reservado para el infinito y la comparación sería desordenada. alineación MSB permitiría la comparación directa de las señales, incluso de diferentes formatos binarios. Dos NaNs con el mismo conjunto de señales de por lo tanto ser equivalente y dar significado a la igualdad.

Debido a que las matemáticas es el campo donde "apenas existen" números. En el cómputo usted debe initialize esos números y guardar su estado de acuerdo a sus necesidades. En esos tiempos de inicialización de la memoria trabajó en las formas que nunca podría confiar. Nunca se podía permitir que el poder pensar en este "Oh, eso sería ser inicializado con 0xCD todo el tiempo, mi algo no se rompió" .

Por lo que necesita adecuada no mezclar disolvente que es lo suficientemente pegajosa a no dejar que su algoritmo de no ser absorbido por y roto. Buenos algoritmos que se usan números son en su mayoría van a trabajar con las relaciones, y los si () se omitirán las relaciones.

Esto es sólo la grasa que se puede poner en nueva variable en la creación, en lugar de programar el infierno azar de la memoria del ordenador. Y el algoritmo sea lo que sea, no se romperá.

A continuación, cuando todavía repente descubrir que su algoritmo está produciendo NaNs, es posible limpiar hacia fuera, mirando en todas las ramas de uno en uno. Una vez más, "siempre es falsa" regla está ayudando mucho en esto.

Para mí, la manera más fácil de explicarlo es:

  

Tengo algo y si no es una manzana entonces es una naranja?

No se puede comparar con otra cosa NaN (incluso él mismo), ya que no tiene un valor. También puede ser cualquier valor (excepto un número).

  

Tengo algo y si no es igual a un número entonces es una cadena?

respuesta muy corta:

Debido a que el siguiente: nan / nan = 1 No debe sostener. De lo contrario sería inf/inf 1.

(Por lo tanto nan no puede ser igual a nan. En cuanto a > o <, si nan respetaría cualquier relación de orden en un conjunto que satisface la propiedad de Arquímedes, habríamos nan / nan = 1 de nuevo en el límite).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top