floating point - punto - ¿Por qué NaN no es igual a NaN?
norma ieee 754 pdf (6)
Bueno, log(-1)
da NaN
, y acos(2)
también da NaN
. ¿Eso significa que log(-1) == acos(2)
? Claramente no. Por lo tanto, tiene perfecto sentido que NaN
no sea igual a sí mismo.
Revisando esto casi dos años más tarde, aquí hay una función de comparación "NaN-safe":
function compare(a,b) {
return a == b || (isNaN(a) && isNaN(b));
}
Esta pregunta ya tiene una respuesta aquí:
El estándar IEEE relevante define una constante numérica NaN (no un número) y prescribe que NaN debe comparar como no igual a sí mismo. ¿Porqué es eso?
Todos los idiomas con los que estoy familiarizado implementan esta regla. Pero a menudo causa problemas importantes, por ejemplo, comportamiento inesperado cuando NaN se almacena en un contenedor, cuando NaN está en los datos que se están ordenando, etc. Sin mencionar que la gran mayoría de los programadores esperan que cualquier objeto sea igual a él ( antes de que aprendan sobre NaN), por lo que sorprenderlos aumenta los errores y la confusión.
Los estándares IEEE están bien pensados, por lo que estoy seguro de que hay una buena razón por la cual la comparación de NaN como igual a sí misma sería mala. Simplemente no puedo entender qué es.
En realidad, hay un concepto en matemáticas conocido como valores de "unidad". Estos valores son extensiones que están cuidadosamente construidas para reconciliar los problemas externos en un sistema. Por ejemplo, puede pensar que el anillo en el infinito en el plano complejo es un punto o un conjunto de puntos, y algunos problemas anteriormente pretenciosos desaparecen. Hay otros ejemplos de esto con respecto a cardinalidades de conjuntos en los que puede demostrar que puede elegir la estructura del continuo de infinitos siempre que | P (A) | > | A | y nada se rompe
DESCARGO DE RESPONSABILIDAD: Solo estoy trabajando con mi vago recuerdo de algunas advertencias interesantes durante mis estudios de matemáticas. Me disculpo si hice un lamentable trabajo al representar los conceptos a los que aludí anteriormente.
Si desea creer que NaN es un valor solitario, entonces probablemente no estará satisfecho con algunos de los resultados, como que el operador de igualdad no funciona de la manera que espera / desea. Sin embargo, si eliges creer que NaN es más un continuo de "maldad" representado por un marcador de posición solitario, entonces estás perfectamente satisfecho con el comportamiento del operador de igualdad. En otras palabras, pierdes de vista al pez que atrapaste en el mar pero atrapas otro que se ve igual pero huele mal.
Mi respuesta original (de hace 4 años) critica la decisión desde la perspectiva moderna sin entender el contexto en el que se tomó la decisión. Como tal, no responde la pregunta.
La respuesta correcta se da here :
NaN
! =NaN
originó a partir de dos consideraciones pragmáticas:[...] No hubo predicado
isnan( )
en el momento en que NaN se formalizó en la aritmética 8087; era necesario proporcionarles a los programadores un medio conveniente y eficiente para detectar valores de NaN que no dependieran de lenguajes de programación que proporcionaran algo así comoisnan( )
que podría llevar muchos años.
Hubo una desventaja en ese enfoque: hizo que NaN fuera menos útil en muchas situaciones no relacionadas con la computación numérica. Por ejemplo, mucho más tarde cuando las personas querían usar NaN
para representar valores perdidos y ponerlos en contenedores basados en hash, no podían hacerlo.
Si el comité previó casos de uso futuros, y los consideró lo suficientemente importantes, ¡podrían haber optado por lo más detallado !(x<x & x>x)
lugar de x!=x
como una prueba para NaN
. Sin embargo, su enfoque fue más pragmático y estrecho: proporcionar la mejor solución para un cálculo numérico, y como tal, no vieron ningún problema con su enfoque.
===
Respuesta original:
Lo siento, por mucho que aprecio el pensamiento que entró en la respuesta más votados, no estoy de acuerdo con eso. NaN no significa "indefinido": consulte http://www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF , página 7 (busque la palabra "indefinido"). Como confirma ese documento, NaN es un concepto bien definido.
Además, el enfoque de IEEE era seguir las reglas de matemáticas regulares tanto como fuera posible, y cuando no podían, seguir la regla de "menor sorpresa" - ver here . Cualquier objeto matemático es igual a sí mismo, por lo que las reglas de las matemáticas implicarían que NaN == NaN debería ser verdadero. No puedo ver ninguna razón válida y poderosa para desviarse de un principio matemático tan importante (sin mencionar las reglas menos importantes de la tricotomía de comparación, etc.).
Como resultado, mi conclusión es la siguiente.
Los miembros del comité de IEEE no pensaron esto claramente y cometieron un error. Dado que muy pocas personas entendían el enfoque del comité IEEE, o se preocupaban por lo que dice exactamente el estándar sobre NaN (a saber: el tratamiento de la mayoría de los compiladores de NaN viola el estándar IEEE de todos modos), nadie dio la alarma. Por lo tanto, este error ahora está incrustado en el estándar. Es poco probable que se solucione, ya que una solución de este tipo podría romper una gran cantidad de código existente.
Editar: Aquí hay una publicación de una discusión muy informativa. Nota: para obtener una vista imparcial, debe leer todo el hilo, ya que Guido toma una visión diferente de la de otros desarrolladores principales. Sin embargo, Guido no está personalmente interesado en este tema, y en gran parte sigue la recomendación de Tim Peters. Si alguien tiene argumentos de Tim Peters a favor de NaN != NaN
, por favor agréguelos en comentarios; ellos tienen una buena oportunidad de cambiar mi opinión.
Prueba esto:
var a = ''asdf'';
var b = null;
var intA = parseInt(a);
var intB = parseInt(b);
console.log(intA); //logs NaN
console.log(intB); //logs NaN
console.log(intA==intB);// logs false
Si intA == intB fuera verdadero, eso podría llevarte a concluir que a == b, lo que claramente no es.
Otra forma de verlo es que NaN solo le brinda información sobre lo que NO ES, no lo que es. Por ejemplo, si digo ''una manzana no es un gorila'' y ''una naranja no es un gorila'', ¿concluiría que ''una manzana'' == ''una naranja''?
Una buena propiedad es: si x == x
devuelve falso, entonces x
es NaN.
(uno puede usar esta propiedad para verificar si x
es NaN
o no).
La respuesta aceptada es 100% sin pregunta INCORRECTA . No está medio equivocado o incluso está un poco equivocado. Me temo que este problema va a confundir y engañar a los programadores durante mucho tiempo por venir cuando esta pregunta aparece en las búsquedas.
NaN está diseñado para propagarse a través de todos los cálculos, infectándolos como un virus, por lo que si en algún lugar de tus cálculos profundos y complejos alcanzas un NaN, no emites una respuesta aparentemente sensata. De lo contrario, por identidad NaN / NaN debería ser igual a 1, junto con todas las demás consecuencias como (NaN / NaN) == 1, (NaN * 1) == NaN, etc. Si imagina que sus cálculos fallaron en algún lugar (el redondeo produjo un cero denominador, produciendo NaN), etc. entonces usted podría obtener resultados muy incorrectos (o peores: sutilmente incorrectos) de sus cálculos sin un indicador obvio de por qué.
También hay muy buenas razones para los NaN en los cálculos al probar el valor de una función matemática; uno de los ejemplos dados en el documento vinculado es encontrar los ceros () de una función f (). Es completamente posible que en el proceso de probar la función con valores de adivinación, pruebe uno en el que la función f () no arroje ningún resultado razonable. Esto permite que ceros () vean el NaN y continúen su trabajo.
La alternativa a NaN es activar una excepción tan pronto como se encuentre una operación ilegal (también llamada señal o trampa). Además de las enormes penalizaciones de rendimiento que podría encontrar, en ese momento no había ninguna garantía de que las CPU lo admitirían en hardware o el sistema operativo / lenguaje lo admitiría en el software; cada uno era su propio copo de nieve único en el manejo de punto flotante. IEEE decidió manejarlo explícitamente en el software como valores de NaN, por lo que sería portátil en cualquier sistema operativo o lenguaje de programación. Los algoritmos correctos de coma flotante son generalmente correctos en todas las implementaciones de coma flotante , ya sea node.js o COBOL (hah).
En teoría, no tiene que establecer directivas específicas de #pragma, establecer banderas de compilador locas, detectar las excepciones correctas o instalar manejadores de señales especiales para hacer que lo que parece ser el algoritmo idéntico realmente funcione correctamente. Desafortunadamente, algunos diseñadores de idiomas y escritores de compiladores han estado muy ocupados deshaciendo esta función lo mejor que pudieron.
Lea parte de la información sobre el historial del punto flotante IEEE 754. También esta respuesta a una pregunta similar en la que un miembro del comité respondió: ¿Cuál es la razón de todas las comparaciones que devuelven resultados falsos para NaEE IEEE754?
"Una entrevista con el viejo hombre de punto flotante"
"Historial del formato de punto flotante IEEE"
Lo que todo científico de la computación debería saber sobre la aritmética de coma flotante