Pourquoi les comparaisons des valeurs de NaN se comportent-elles différemment de toutes les autres valeurs? Autrement dit, toutes les comparaisons avec les opérateurs ==, <=,> =, <,> où une ou les deux valeurs sont NaN renvoie false, contrairement au comportement de toutes les autres valeurs.
Je suppose que cela simplifie les calculs numériques d'une certaine manière, mais je n'ai pas trouvé de raison explicite, même pas dans les notes de cours sur le statut de l'IEEE 754 de Kahan qui discutent en détail d'autres décisions de conception.
Ce comportement déviant cause des problèmes lors d'un traitement de données simple. Par exemple, lors du tri d'une liste d'enregistrements par rapport à un champ à valeur réelle dans un programme C, j'ai besoin d'écrire du code supplémentaire pour gérer NaN comme élément maximal, sinon l'algorithme de tri pourrait devenir confus.
Edit: Jusqu'à présent, les réponses affirment toutes qu'il est inutile de comparer les NaN.
Je suis d'accord, mais cela ne signifie pas que la bonne réponse est fausse, ce serait plutôt un Not-a-Boolean (NaB), qui n'existe heureusement pas.
Donc, le choix de renvoyer vrai ou faux pour les comparaisons est à mon avis arbitraire, et pour le traitement général des données, il serait avantageux qu'il obéisse aux lois habituelles (réflexivité de ==, trichotomie de <, ==,>), de peur que les structures de données qui s'appuient sur ces lois se confondent.
Je demande donc un avantage concret à enfreindre ces lois, pas seulement un raisonnement philosophique.
Edit 2: Je pense que je comprends maintenant pourquoi rendre NaN maximal serait une mauvaise idée, cela gâcherait le calcul des limites supérieures.
NaN! = NaN peut être souhaitable pour éviter de détecter la convergence dans une boucle telle que
while (x != oldX) {
oldX = x;
x = better_approximation(x);
}
qui devrait cependant être mieux écrit en comparant la différence absolue avec une petite limite. Donc à mon humble avis, c'est un argument relativement faible pour briser la réflexivité à NaN.
la source
while (fabs(x - oldX) > threshold)
, sortant de la boucle si la convergence se produit ou si un NaN entre dans le calcul. La détection du NaN et du remède approprié se ferait alors en dehors de la boucle.Réponses:
J'étais membre du comité IEEE-754, je vais essayer de clarifier un peu les choses.
Tout d'abord, les nombres à virgule flottante ne sont pas des nombres réels, et l'arithmétique à virgule flottante ne satisfait pas les axiomes de l'arithmétique réelle. La trichotomie n'est pas la seule propriété de l'arithmétique réelle qui ne tient pas pour les flotteurs, ni même la plus importante. Par exemple:
Je pourrais continuer. Il n'est pas possible de spécifier un type arithmétique de taille fixe qui satisfait toutes les propriétés de l'arithmétique réelle que nous connaissons et aimons. Le comité 754 doit décider de plier ou de casser certains d'entre eux. Ceci est guidé par des principes assez simples:
En ce qui concerne votre commentaire "cela ne signifie pas que la bonne réponse est fausse", c'est faux. Le prédicat
(y < x)
demande siy
est inférieur àx
. Siy
est NaN, il n'est pas inférieur à toute valeur à virgule flottantex
, donc la réponse est nécessairement fausse.J'ai mentionné que la trichotomie ne s'applique pas aux valeurs à virgule flottante. Cependant, il existe une propriété similaire qui tient. Article 5.11, paragraphe 2 de la norme 754-2008:
En ce qui concerne l'écriture de code supplémentaire pour gérer les NaN, il est généralement possible (mais pas toujours facile) de structurer votre code de manière à ce que les NaN passent correctement, mais ce n'est pas toujours le cas. Dans le cas contraire, un code supplémentaire peut être nécessaire, mais c'est un petit prix à payer pour la commodité que la fermeture algébrique a apportée à l'arithmétique à virgule flottante.
Addendum: De nombreux commentateurs ont fait valoir qu'il serait plus utile de préserver la réflexivité de l'égalité et de la trichotomie au motif que l'adoption de NaN! = NaN ne semble conserver aucun axiome familier. J'avoue avoir une certaine sympathie pour ce point de vue, alors j'ai pensé que je reviendrais sur cette réponse et fournirais un peu plus de contexte.
D'après ce que j'ai compris en parlant à Kahan, NaN! = NaN est né de deux considérations pragmatiques:
Cela
x == y
devrait être équivalent àx - y == 0
chaque fois que possible (au-delà d'être un théorème de l'arithmétique réelle, cela rend l'implémentation matérielle de la comparaison plus efficace en termes d'espace, ce qui était de la plus haute importance au moment où la norme a été élaborée - notez cependant que cela est violé pour x = y = l'infini, ce n'est donc pas une bonne raison en soi; il aurait pu être raisonnablement plié(x - y == 0) or (x and y are both NaN)
).Plus important encore, il n'y avait pas de
isnan( )
prédicat 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 des langages de programmation fournissant quelque chose comme celaisnan( )
qui pourrait prendre de nombreuses années. Je citerai les propres écrits de Kahan sur le sujet:Notez que c'est également la logique qui exclut le retour de quelque chose comme un «Not-A-Boolean». Peut-être que ce pragmatisme était déplacé et que la norme aurait dû être exigée
isnan( )
, mais cela aurait rendu NaN presque impossible à utiliser efficacement et commodément pendant plusieurs années alors que le monde attendait l'adoption du langage de programmation. Je ne suis pas convaincu que cela aurait été un compromis raisonnable.Pour être franc: le résultat de NaN == NaN ne va pas changer maintenant. Mieux vaut apprendre à vivre avec que de se plaindre sur Internet. Si vous voulez faire valoir qu'une relation d'ordre appropriée pour les conteneurs devrait également exister, je recommanderais de recommander que votre langage de programmation préféré implémente le
totalOrder
prédicat normalisé dans IEEE-754 (2008). Le fait qu'il n'ait pas déjà témoigné de la validité de l'inquiétude de Kahan qui a motivé l'état actuel des choses.la source
1f/3f == 10000001f/30000002f
? Si les valeurs à virgule flottante sont considérées comme des classes d'équivalence,a=b
cela ne signifie pas "Les calculs qui ont donnéa
etb
, s'ils étaient effectués avec une précision infinie, donneraient des résultats identiques", mais plutôt "Ce que l'on saita
correspond à ce que l'on saitb
". Je suis curieux si vous connaissez des exemples de code où avoir "Nan! = NaN" rend les choses plus simples qu'elles ne le seraient autrement?!(x < 0 || x == 0 || x > 0)
, mais cela aurait été plus lent et plus maladroit quex != x
.NaN peut être considéré comme un état / nombre indéfini. similaire au concept de 0/0 non défini ou sqrt (-3) (dans le système de nombres réels où vit la virgule flottante).
NaN est utilisé comme une sorte d'espace réservé pour cet état indéfini. Mathématiquement parlant, undefined n'est pas égal à undefined. Vous ne pouvez pas non plus dire qu'une valeur non définie est supérieure ou inférieure à une autre valeur non définie. Par conséquent, toutes les comparaisons renvoient faux.
Ce comportement est également avantageux dans les cas où vous comparez sqrt (-3) à sqrt (-2). Ils renverraient tous les deux NaN mais ils ne sont pas équivalents même s'ils renvoient la même valeur. Par conséquent, avoir toujours un retour égal à faux dans le cas de NaN est le comportement souhaité.
la source
!=
opérateur renvoie true. AvoirNaN==NaN
et lesNaN!=NaN
deux renvoyer false permettrait au code qui compare x et y de choisir ce qui devrait se produire lorsque les deux opérandes sont NaN en choisissant soit==
ou!=
.Pour jeter encore une autre analogie. Si je vous remets deux boîtes et que je vous dis qu'aucune d'entre elles ne contient une pomme, me diriez-vous que les boîtes contiennent la même chose?
NaN ne contient aucune information sur ce qu'est quelque chose, juste ce qu'elle n'est pas. Par conséquent, ces éléments ne peuvent jamais être considérés comme absolument égaux.
la source
(NaN==Nan)==false
. Ce que je ne comprends pas, c'est la justification(Nan!=Nan)==true
.D'après l'article de wikipedia sur NaN , les pratiques suivantes peuvent provoquer des NaN:
Puisqu'il n'y a aucun moyen de savoir laquelle de ces opérations a créé le NaN, il n'y a aucun moyen de les comparer qui ait du sens.
la source
Je ne connais pas la justification de la conception, mais voici un extrait de la norme IEEE 754-1985:
"Il doit être possible de comparer des nombres à virgule flottante dans tous les formats pris en charge, même si les formats des opérandes diffèrent. Les comparaisons sont exactes et ne débordent ni ne dépassent jamais. Quatre relations mutuellement exclusives sont possibles: inférieure à, égale, supérieure à et non ordonnée Le dernier cas se présente quand au moins un opérande est NaN. Chaque NaN doit être comparé sans ordre avec tout, y compris lui-même. "
la source
Cela ne semble particulier que parce que la plupart des environnements de programmation qui autorisent les NaN ne permettent pas également la logique à 3 valeurs. Si vous lancez une logique à 3 valeurs dans le mélange, cela devient cohérent:
Même .NET ne fournit pas d'
bool? operator==(double v1, double v2)
opérateur, vous êtes donc toujours coincé avec le(NaN == NaN) = false
résultat stupide .la source
Je suppose que NaN (Not A Number) signifie exactement cela: ce n'est pas un nombre et donc le comparer n'a pas vraiment de sens.
C'est un peu comme l'arithmétique en SQL avec des
null
opérandes: ils résultent tous ennull
.Les comparaisons pour les nombres à virgule flottante comparent les valeurs numériques. Ainsi, ils ne peuvent pas être utilisés pour des valeurs non numériques. NaN ne peut donc pas être comparé au sens numérique.
la source
FOO
. Pour que NaN soit équivalent,if (NaN != NaN) foo();
il ne devrait pas s'exécuterfoo
, mais c'est le cas.La réponse trop simplifiée est qu'un NaN n'a pas de valeur numérique, il n'y a donc rien à comparer avec quoi que ce soit d'autre.
Vous pouvez envisager de tester et de remplacer vos NaN par + INF si vous voulez qu'ils agissent comme + INF.
la source
Bien que je convienne que les comparaisons de NaN avec n'importe quel nombre réel ne devraient pas être ordonnées, je pense qu'il y a une raison valable de comparer NaN avec lui-même. Comment, par exemple, découvre-t-on la différence entre les NaN de signalisation et les NaN silencieux? Si nous considérons les signaux comme un ensemble de valeurs booléennes (c'est-à-dire un vecteur binaire), on pourrait bien se demander si les vecteurs binaires sont identiques ou différents et ordonner les ensembles en conséquence. Par exemple, lors du décodage d'un exposant biaisé maximal, si le significand était déplacé vers la gauche de manière à aligner le bit le plus significatif du significand sur le bit le plus significatif du format binaire, une valeur négative serait un NaN silencieux et toute valeur positive serait être un NaN de signalisation. Le zéro est bien sûr réservé à l'infini et la comparaison ne serait pas ordonnée. L'alignement MSB permettrait la comparaison directe des signaux même à partir de différents formats binaires. Deux NaN avec le même ensemble de signaux seraient donc équivalents et donneraient sens à l'égalité.
la source
Pour moi, la façon la plus simple de l'expliquer est:
Vous ne pouvez pas comparer NaN avec autre chose (même lui-même) car il n'a pas de valeur. Il peut également s'agir de n'importe quelle valeur (sauf un nombre).
la source
Parce que les mathématiques sont le domaine où les nombres "n'existent que". En informatique, vous devez initialiser ces nombres et conserver leur état en fonction de vos besoins. À cette époque, l'initialisation de la mémoire fonctionnait d'une manière sur laquelle vous ne pouviez jamais compter. Vous ne pourriez jamais vous permettre de penser à ce "oh, ce serait initialisé avec 0xCD tout le temps, mon algo ne se cassera pas" .
Vous avez donc besoin d'un solvant non mélangeur approprié qui est suffisamment collant pour ne pas laisser votre algorithme être aspiré et cassé. Les bons algorithmes impliquant des nombres vont surtout fonctionner avec des relations, et ceux if () seront omis.
C'est juste de la graisse que vous pouvez mettre dans une nouvelle variable lors de la création, au lieu de programmer l'enfer aléatoire de la mémoire de l'ordinateur. Et votre algorithme, quel qu'il soit, ne se cassera pas.
Ensuite, lorsque vous découvrez soudainement que votre algorithme produit des NaN, il est possible de le nettoyer, en examinant chaque branche une par une. Encore une fois, la règle "toujours faux" aide beaucoup à cela.
la source
Réponse très courte:
Parce que ce qui suit: ne
nan / nan = 1
doit PAS tenir. Sinon, ceinf/inf
serait 1.(Par conséquent,
nan
ne peut pas être égal ànan
. Quant à>
ou<
, sinan
respecterait toute relation d'ordre dans un ensemble satisfaisant la propriété d'Archimède, nous aurions à nouveaunan / nan = 1
à la limite).la source
inf = inf
etinf / inf = nan
, doncnan = nan
n'empêchera pas nonnan / nan = nan
plus.nan / nan = 1
? Quoi qu'il en soit ... Votre raisonnement a du sens si inf et nan étaient comme tous les autres nombres. Ce n'est pas le cas. La raison pour laquelleinf/inf
doit êtrenan
(ou forme indéterminée en mathématiques) et non1
est plus subtile que la simple manipulation algébrique (voir le théorème de L'Hospital).