web-dev-qa-db-fra.com

Pourquoi NaN n'est-il pas égal à NaN?

La norme IEEE pertinente définit une constante numérique NaN (pas un nombre) et prescrit que NaN doit comparer comme non égal à lui-même. Pourquoi donc?

Toutes les langues que je connais implémentent cette règle. Mais cela provoque souvent des problèmes importants, par exemple un comportement inattendu lorsque NaN est stocké dans un conteneur, lorsque NaN se trouve dans les données en cours de tri, etc. Sans oublier, la grande majorité des programmeurs s'attendent à ce que tout objet soit égal à lui-même ( avant d'en savoir plus sur NaN), les surprendre ajoute aux bugs et à la confusion.

Les normes IEEE sont bien pensées, donc je suis sûr qu'il y a une bonne raison pour laquelle une comparaison NaN égale à elle-même serait mauvaise. Je n'arrive pas à comprendre ce que c'est.

108
max

Ma réponse originale (d'il y a 4 ans) critique la décision du point de vue des temps modernes sans comprendre le contexte dans lequel la décision a été prise. En tant que tel, il ne répond pas à la question.

La bonne réponse est donnée ici :

NaN! = NaN provient de deux considérations pragmatiques:

[...] Il n'y avait pas de prédicat isnan( ) au moment où NaN a été formalisé dans l'arithmétique 8087; il était nécessaire de fournir aux programmeurs un moyen pratique et efficace de détecter les valeurs NaN qui ne dépendaient pas de langages de programmation fournissant quelque chose comme isnan( ) qui pourrait prendre plusieurs années

Cette approche présente un inconvénient: elle rend le NaN moins utile dans de nombreuses situations sans rapport avec le calcul numérique. Par exemple, beaucoup plus tard, lorsque les gens voulaient utiliser NaN pour représenter les valeurs manquantes et les mettre dans des conteneurs basés sur le hachage, ils ne pouvaient pas le faire.

Si le comité avait prévu de futurs cas d'utilisation et les avait jugés suffisamment importants, il aurait pu opter pour la !(x<x & x>x) plus verbeuse au lieu de x!=x comme test pour NaN. Cependant, leur objectif était plus pragmatique et étroit: fournir la meilleure solution pour un calcul numérique, et en tant que tel, ils ne voyaient aucun problème avec leur approche.

===

Réponse originale:

Je suis désolé, bien que j'apprécie la pensée qui a motivé la réponse la plus votée, je ne suis pas d'accord. NaN ne signifie pas "non défini" - voir http://www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF , page 7 (recherchez le mot "non défini"). Comme le confirme ce document, le NaN est un concept bien défini.

De plus, l'approche IEEE consistait à suivre autant que possible les règles mathématiques habituelles et, dans le cas contraire, à respecter la règle de la "moindre surprise" - voir https://stackoverflow.com/a/1573715/336527 . Tout objet mathématique est égal à lui-même, donc les règles des mathématiques impliqueraient que NaN == NaN devrait être True. Je ne vois aucune raison valable et puissante de s'écarter d'un principe mathématique aussi important (sans parler des règles moins importantes de la trichotomie de comparaison, etc.).

Par conséquent, ma conclusion est la suivante.

Les membres du comité de l'IEEE n'y ont pas réfléchi très clairement et ont fait une erreur. Étant donné que très peu de gens ont compris l'approche du comité IEEE ou se sont inquiétés de ce que la norme dit exactement à propos de NaN (à savoir: le traitement de la plupart des compilateurs de NaN viole la norme IEEE de toute façon), personne n'a sonné l'alarme. Par conséquent, cette erreur est maintenant intégrée dans la norme. Il est peu probable qu'il soit corrigé, car un tel correctif briserait beaucoup de code existant.

Edit: Voici un article d'une discussion très informative. Remarque: pour obtenir une vue impartiale, vous devez lire l'intégralité du fil, car Guido adopte une vue différente de celle de certains autres développeurs principaux. Cependant, Guido n'est pas personnellement intéressé par ce sujet et suit largement la recommandation de Tim Peters. Si quelqu'un a des arguments de Tim Peters en faveur de NaN != NaN, veuillez les ajouter dans les commentaires; ils ont une bonne chance de changer mon opinion.

29
max

La réponse acceptée est 100% sans question FAUX . Pas à moitié faux ou même légèrement faux. Je crains que ce problème ne perturbe et n'induise les programmeurs en erreur pendant longtemps lorsque cette question apparaîtra dans les recherches.

Le NaN est conçu pour se propager à travers tous les calculs, les infectant comme un virus, donc si quelque part dans vos calculs profonds et complexes vous frappez un NaN, vous ne développez pas une réponse apparemment sensée. Sinon, par identité, NaN/NaN devrait être égal à 1, avec toutes les autres conséquences comme (NaN/NaN) == 1, (NaN * 1) == NaN, etc. Si vous imaginez que vos calculs ont mal tourné quelque part (l'arrondi a produit un dénominateur zéro, donnant NaN), etc., vous pourriez obtenir des résultats extrêmement incorrects (ou pire: subtilement incorrects) à partir de vos calculs sans aucun indicateur évident de pourquoi.

Il y a aussi de très bonnes raisons pour les NaN dans les calculs lors du sondage de la valeur d'une fonction mathématique; l'un des exemples donnés dans le document lié est de trouver les zéros () d'une fonction f (). Il est tout à fait possible que lors du test de la fonction avec des valeurs de supposition, vous en sondiez une où la fonction f() ne donne aucun résultat sensible. Cela permet à zéros () de voir le NaN et poursuivre ses travaux.

L'alternative à NaN est de déclencher une exception dès qu'une opération illégale est rencontrée (également appelée signal ou piège). Outre les pénalités de performances massives que vous pourriez rencontrer, à l'époque, il n'y avait aucune garantie que les CPU le prendraient en charge en matériel ou que le système d'exploitation/langage le prendrait en charge en logiciel; chacun était son propre flocon de neige unique dans la gestion des virgules flottantes. L'IEEE a décidé de le traiter explicitement dans le logiciel en tant que valeurs NaN afin qu'il soit portable sur n'importe quel système d'exploitation ou langage de programmation. Les algorithmes à virgule flottante corrects sont généralement corrects dans toutes les implémentations à virgule flottante , que ce soit node.js ou COBOL (hah).

En théorie, vous n'avez pas besoin de définir des directives #pragma spécifiques, de définir des drapeaux de compilation fous, d'intercepter les exceptions correctes ou d'installer des gestionnaires de signaux spéciaux pour que ce qui semble être l'algorithme identique fonctionne correctement. Malheureusement, certains concepteurs de langages et rédacteurs de compilateurs ont été très occupés à annuler cette fonctionnalité au mieux de leurs capacités.

Veuillez lire certaines informations sur l'historique de la virgule flottante IEEE 754. Aussi, cette réponse à une question similaire à laquelle un membre du comité a répondu: Quelle est la justification de toutes les comparaisons retournant des valeurs fausses pour les valeurs NaN IEEE754?

"Une entrevue avec le vieil homme de virgule flottante"

"Historique du format à virgule flottante IEEE"

Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante

143
russbishop

Eh bien, log(-1) donne NaN, et acos(2) donne également NaN. Est-ce à dire que log(-1) == acos(2)? Pas du tout. Il est donc parfaitement logique que NaN ne soit pas égal à lui-même.

Revisitant cela presque deux ans plus tard, voici une fonction de comparaison "sans NaN":

function compare(a,b) {
    return a == b || (isNaN(a) && isNaN(b));
}
97

Une belle propriété est: si x == x renvoie false, puis x est NaN.

(on peut utiliser cette propriété pour vérifier si x est NaN ou non.)

8
asf107

Essaye ça:

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 était vrai, cela pourrait vous amener à conclure que a == b, ce qui n'est clairement pas le cas.

Une autre façon de voir les choses est que NaN vous donne simplement des informations sur ce que quelque chose N'EST PAS, pas ce que c'est. Par exemple, si je dis "un Apple n'est pas un gorille" et "une orange n'est pas un gorille", concluriez-vous que "une pomme" == "une orange"?

7
Mike C

En fait, il existe un concept en mathématiques connu sous le nom de valeurs "d'unité". Ces valeurs sont des extensions soigneusement conçues pour réconcilier les problèmes périphériques d'un système. Par exemple, vous pouvez penser à l'anneau à l'infini dans le plan complexe comme étant un point ou un ensemble de points, et certains problèmes autrefois prétentieux disparaissent. Il existe d'autres exemples de cela en ce qui concerne les cardinalités d'ensembles où vous pouvez démontrer que vous pouvez choisir la structure du continuum des infinis tant que | P (A) | > | A | et rien ne casse.

AVERTISSEMENT: Je travaille uniquement avec ma vague mémoire de mes quelques mises en garde intéressantes pendant mes études de mathématiques. Je m'excuse si j'ai fait un travail affreux de représenter les concepts auxquels j'ai fait allusion ci-dessus.

Si vous voulez croire que NaN est une valeur solitaire, alors vous allez probablement être mécontent de certains résultats comme l'opérateur d'égalité qui ne fonctionne pas comme vous l'attendez/le souhaitez. Cependant, si vous choisissez de croire que NaN est davantage un continuum de "méchanceté" représenté par un espace réservé solitaire, alors vous êtes parfaitement satisfait du comportement de l'opérateur d'égalité. En d'autres termes, vous perdez de vue le poisson que vous avez pêché dans la mer mais vous en attrapez un autre qui a la même apparence mais qui est tout aussi malodorant.

2
Garth Pickell