¿Por qué las comparaciones de los valores de NaN se comportan de manera diferente a todos los demás 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 los cálculos numéricos de alguna manera, pero no pude encontrar una razón explícitamente expuesta, ni siquiera en las Lecture Notes on the Status of IEEE 754 de Kahan que discute otras decisiones de diseño en detalle.
Este comportamiento desviado está causando problemas al realizar un procesamiento de datos simple. Por ejemplo, al ordenar una lista de registros con algún campo de valor real en un programa en C, necesito escribir un código adicional para manejar NaN como elemento máximo, de lo contrario, el algoritmo de clasificación podría confundirse.
Editar: Las respuestas hasta ahora argumentan que no tiene sentido comparar NaNs.
Estoy de acuerdo, pero eso no significa que la respuesta correcta sea falsa, sino que sería un Not-a-Boolean (NaB), que afortunadamente no existe.
Por lo tanto, la elección de devolver verdadero o falso para las comparaciones es, en mi opinión, arbitraria, y para el procesamiento general de datos sería ventajoso si obedeciera las leyes habituales (reflexividad de ==, tricotomía de <, ==,>), no sea que las estructuras de datos que confían en estas leyes se confunden.
Entonces, pido una ventaja concreta de romper estas leyes, no solo el razonamiento filosófico.
Edición 2: Creo que ahora entiendo por qué hacer que NaN sea máximo sería una mala idea, dañaría el cálculo de los límites superiores.
NaN! = NaN podría ser deseable para evitar detectar la convergencia en un bucle como
while (x != oldX) {
oldX = x;
x = better_approximation(x);
}
que, sin embargo, debería escribirse mejor comparando la diferencia absoluta con un límite pequeño. En mi humilde opinión, este es un argumento relativamente débil para romper la reflexividad en NaN.
fuente
while (fabs(x - oldX) > threshold)
salir del bucle si se produce la convergencia o si un NaN ingresa al cálculo. La detección del NaN y el remedio apropiado se realizarían fuera del circuito.Respuestas:
Fui miembro del comité IEEE-754, intentaré ayudar a aclarar un poco las cosas.
En primer lugar, los números de coma flotante no son números reales, y la aritmética de coma flotante no satisface los axiomas de la aritmética real. La tricotomía no es la única propiedad de la aritmética real que no es válida para las carrozas, ni siquiera la más importante. Por ejemplo:
Podría seguir. No es posible especificar un tipo aritmético de tamaño fijo que satisfaga todas las propiedades de la aritmética real que conocemos y amamos. El comité 754 tiene que decidir doblar o romper algunos de ellos. Esto se guía por algunos principios bastante simples:
Con respecto a su comentario "eso no significa que la respuesta correcta sea falsa", esto está mal. El predicado
(y < x)
pregunta siy
es menor quex
. Siy
es NaN, entonces es no menos que cualquier valor de punto flotantex
, por lo que la respuesta es necesariamente falsa.Mencioné que la tricotomía no es válida para los valores de coma flotante. Sin embargo, hay una propiedad similar que se mantiene. Cláusula 5.11, párrafo 2 de la norma 754-2008:
En cuanto a escribir código adicional para manejar NaNs, generalmente es posible (aunque no siempre es fácil) estructurar su código de tal manera que los NaNs no funcionen correctamente, pero este no es siempre el caso. Cuando no lo es, puede ser necesario algún código adicional, pero ese es un pequeño precio a pagar por la conveniencia que el cierre algebraico trajo a la aritmética de punto flotante.
Anexo: Muchos comentaristas han argumentado que sería más útil preservar la reflexividad de la igualdad y la tricotomía sobre la base de que adoptar NaN! = NaN no parece preservar ningún axioma familiar. Confieso tener cierta simpatía por este punto de vista, así que pensé en volver a visitar esta respuesta y proporcionar un poco más de contexto.
Mi comprensión al hablar con Kahan es que NaN! = NaN se originó a partir de dos consideraciones pragmáticas:
Eso
x == y
debería ser equivalente ax - 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 sea más eficiente en el espacio, lo que era de suma importancia en el momento en que se desarrolló el estándar; tenga en cuenta, sin embargo, que esto se viola para x = y = infinito, por lo que no es una gran razón en sí misma; podría haberse doblado razonablemente(x - y == 0) or (x and y are both NaN)
).Más importante aún, no había un
isnan( )
predicado en el momento en que NaN se formalizó en la aritmética 8087; era necesario proporcionar a los programadores un medio conveniente y eficiente para detectar valores de NaN que no dependieran de que los lenguajes de programación proporcionaran algo como loisnan( )
que podría llevar muchos años. Citaré los propios escritos de Kahan sobre el tema:Tenga en cuenta que esta es también la lógica que descarta devolver algo así como un "No es un booleano". Tal vez este pragmatismo estaba fuera de lugar, y el estándar debería haber requerido
isnan( )
, pero eso habría hecho que NaN fuera casi imposible de usar de manera eficiente y conveniente durante varios años mientras el mundo esperaba la adopción del lenguaje de programación. No estoy convencido de que hubiera sido una compensación razonable.Para ser franco: el resultado de NaN == NaN no va a cambiar ahora. Es mejor aprender a vivir con eso que quejarse en Internet. Si desea argumentar que también debería existir una relación de orden adecuada para contenedores , recomendaría recomendar que su lenguaje de programación favorito implemente el
totalOrder
predicado estandarizado en IEEE-754 (2008). El hecho de que aún no ha demostrado la validez de la preocupación de Kahan que motivó el estado actual de las cosas.fuente
1f/3f == 10000001f/30000002f
? Si los valores de coma flotante se consideran clases de equivalencia, entoncesa=b
no significa "Los cálculos que produjerona
yb
, si se realizan con precisión infinita, arrojarían resultados idénticos", sino más bien "Lo que se sabea
coincide con lo que se sabe sobreb
". Tengo curiosidad si conoces algún ejemplo de código donde tener "Nan! = NaN" hace las cosas más simples de lo que serían de otra manera.!(x < 0 || x == 0 || x > 0)
, pero habría sido más lento y más torpe quex != x
.NaN puede considerarse como un estado / número indefinido. similar al concepto de 0/0 sin definir o sqrt (-3) (en el sistema de números reales donde vive el punto flotante).
NaN se utiliza como una especie de marcador de posición para este estado indefinido. Matemáticamente hablando, indefinido no es igual a indefinido. Tampoco puede decir que un valor indefinido es mayor o menor que otro valor indefinido. Por lo tanto, todas las comparaciones devuelven falso.
Este comportamiento también es ventajoso en los casos en que compara sqrt (-3) con sqrt (-2). Ambos devolverían NaN pero no son equivalentes a pesar de que devuelven el mismo valor. Por lo tanto, tener igualdad siempre devolviendo falso cuando se trata de NaN es el comportamiento deseado.
fuente
!=
operador devuelve verdadero. TenerNaN==NaN
yNaN!=NaN
ambos devolver falso permitiría que el código que compara x e y elija lo que debería suceder cuando ambos operandos son NaN eligiendo==
o!=
.Para agregar otra analogía más. Si te entrego dos cajas y te digo que ninguna de ellas contiene una manzana, ¿me dirías que las cajas contienen la misma cosa?
NaN no contiene información sobre qué es algo, solo qué no es. Por lo tanto, nunca se puede decir que estos elementos sean iguales.
fuente
(NaN==Nan)==false
. Lo que no entiendo es la razón de ser(Nan!=Nan)==true
.Del artículo de Wikipedia sobre NaN , las siguientes prácticas pueden causar NaNs:
Como no hay forma de saber cuál de estas operaciones creó el NaN, no hay forma de compararlas que tenga sentido.
fuente
No conozco la lógica del diseño, pero aquí hay un extracto del estándar IEEE 754-1985:
"Será posible comparar números de coma flotante en todos los formatos admitidos, incluso si los formatos de los operandos difieren. Las comparaciones son exactas y nunca se desbordan ni se desbordan. Son posibles cuatro relaciones mutuamente excluyentes: menor que, igual, mayor que y desordenado . El último caso surge cuando al menos un operando es NaN. Cada NaN se comparará desordenado con todo, incluido él mismo ".
fuente
Solo parece peculiar porque la mayoría de los entornos de programación que permiten NaN no permiten también la lógica de 3 valores. Si arroja lógica de 3 valores en la mezcla, se vuelve consistente:
Incluso .NET no proporciona un
bool? operator==(double v1, double v2)
operador, por lo que todavía está atascado con el(NaN == NaN) = false
resultado tonto .fuente
Supongo que NaN (no es un número) significa exactamente eso: esto no es un número y, por lo tanto, compararlo no tiene sentido.
Es un poco como la aritmética en SQL con
null
operandos: todos resultan ennull
.Las comparaciones para números de coma flotante comparan valores numéricos. Por lo tanto, no se pueden usar para valores no numéricos. NaN, por lo tanto, no se puede comparar en sentido numérico.
fuente
FOO
. Para que NaN sea equivalenteif (NaN != NaN) foo();
no debe ejecutarsefoo
, pero lo hace.La respuesta simplificada es que un NaN no tiene un valor numérico, por lo que no hay nada en él para compararlo con nada más.
Puede considerar probar y reemplazar sus NaNs con + INF si desea que actúen como + INF.
fuente
Si bien estoy de acuerdo en que las comparaciones de NaN con cualquier número real no deberían estar ordenadas, creo que solo hay motivos para comparar NaN consigo mismo. ¿Cómo, por ejemplo, uno descubre la diferencia entre la señalización de NaNs y NaNs silenciosos? Si pensamos en las señales como un conjunto de valores booleanos (es decir, un vector de bits), uno podría preguntarse si los vectores de bits son iguales o diferentes y ordenar los conjuntos en consecuencia. Por ejemplo, al decodificar un exponente sesgado máximo, si el significado se dejara desplazado para alinear el bit más significativo del significado con el bit más significativo del formato binario, un valor negativo sería un NaN silencioso y cualquier valor positivo sería ser una señalización NaN. Cero, por supuesto, está reservado para el infinito y la comparación no estaría ordenada. La alineación de MSB permitiría la comparación directa de señales incluso desde diferentes formatos binarios. Por lo tanto, dos NaN con el mismo conjunto de señales serían equivalentes y darían sentido a la igualdad.
fuente
Para mí, la forma más fácil de explicarlo es:
No puede comparar NaN con otra cosa (incluso ella misma) porque no tiene un valor. También puede ser cualquier valor (excepto un número).
fuente
Porque las matemáticas son el campo donde los números "simplemente existen". En informática, debe inicializar esos números y mantener su estado de acuerdo con sus necesidades. En aquellos viejos tiempos, la inicialización de la memoria funcionaba de formas en las que nunca se podía confiar. Nunca podría permitirse pensar en este "oh, eso se inicializaría con 0xCD todo el tiempo, mi algo no se romperá" .
Por lo tanto, necesita un solvente adecuado que no se mezcle , que sea lo suficientemente pegajoso como para no dejar que su algoritmo sea absorbido y roto. Los buenos algoritmos que involucran números en su mayoría funcionarán con relaciones, y aquellos que () relaciones serán omitidos.
Esto es solo grasa que puedes poner en una nueva variable en la creación, en lugar de programar un infierno aleatorio desde la memoria de la computadora. Y su algoritmo sea lo que sea, no se romperá.
Luego, cuando de repente descubres que tu algoritmo está produciendo NaN, es posible limpiarlo, examinando cada rama de una en una. Nuevamente, la regla "siempre falsa" está ayudando mucho en esto.
fuente
Respuesta muy corta:
Porque lo siguiente:
nan / nan = 1
NO debe sostenerse. Deinf/inf
lo contrario sería 1.(Por
nan
lo tanto , no puede ser igual anan
. En cuanto a ,>
o<
sinan
respetaría cualquier relación de orden en un conjunto que satisface la propiedad de Arquímedes, tendríamos nuevamentenan / nan = 1
en el límite).fuente
inf = inf
yinf / inf = nan
, pornan = nan
lo tanto , tampoco lo impediremosnan / nan = nan
.nan / nan = 1
? De todos modos ... Su razonamiento tiene sentido si inf y nan eran como cualquier otro número. No es el caso. La razón por la queinf/inf
debe sernan
(o forma indeterminada en matemáticas) y no1
es más sutil que la simple manipulación algebraica (ver el teorema de De L'Hospital).