Question

Pourquoi les comparaisons de valeurs NaN se comportent différemment de toutes les autres valeurs? Autrement dit, toutes les comparaisons avec les opérateurs ==, <=,> =, <,> où une ou deux valeurs est NaN retourne false, contrairement au comportement de toutes les autres valeurs.

Je suppose que cela simplifie les calculs numériques d'une certaine façon, mais je ne pouvais pas trouver une raison explicite, pas même dans le les notes de cours sur le statut de l'IEEE 754 par Kahan qui traite d'autres décisions de conception en détail.

Ce comportement déviant est à l'origine des problèmes lorsque vous effectuez un traitement simple des données. Par exemple, lorsque le tri d'une liste d'enregistrements w.r.t. 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: Les réponses à ce jour tous soutiennent qu'il est inutile de comparer NaN.

Je suis d'accord, mais cela ne signifie pas que la bonne réponse est fausse, plutôt que ce serait un non-a-booléenne (NAE), qui n'existe heureusement pas.

Ainsi, le choix de retourner vrai ou faux pour les comparaisons est à mon avis arbitraire, et pour les données générales le traitement, il serait avantageux si elle obéit aux lois habituelles (Réflexivité de ==, trichotomie de <==,>), de peur que les structures de données qui reposent sur ces lois deviennent confus.

Je demande donc un certain avantage concret de briser ces lois, non seulement le raisonnement philosophique.

Edit 2: Je pense que je comprends maintenant pourquoi faire serait maximale NaN une mauvaise idée, ce serait gâcher le calcul des limites supérieures.

NaN! = NaN pourrait être souhaitable d'éviter la détection de 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 cela est un argument relativement faible pour briser réflexivité à NaN.

Était-ce utile?

La solution

J'étais membre du comité IEEE-754, je vais essayer de clarifier les choses un peu.

Tout d'abord, les nombres à virgule flottante ne sont pas des nombres réels, et l'arithmétique à virgule flottante ne satisfait pas aux axiomes de l'arithmétique réelle. Trichotomy n'est pas la seule propriété de l'arithmétique réelle qui ne tient pas pour les flotteurs, ni même le plus important. Par exemple:

  • L'addition est non associative.
  • La loi distributive ne tient pas.
  • Il y a des nombres à virgule flottante sans inverses.

Je pourrais continuer. Il est impossible de spécifier un type arithmétique de taille fixe qui satisfait tous des propriétés de l'arithmétique réelle que nous connaissons et aimons. Le comité 754 doit décider de se plier ou briser certains d'entre eux. Ceci est guidé par des principes assez simples:

  1. Quand nous pouvons, nous correspondre au comportement de l'arithmétique réelle.
  2. Lorsque nous ne pouvons pas, nous essayons de rendre les violations aussi prévisibles et aussi faciles à diagnostiquer que possible.

En ce qui concerne votre commentaire « qui ne veut pas dire que la bonne réponse est fausse », cela est faux. Le (y < x) prédicat demande si y est inférieure à x. Si y est NaN, alors il est pas moins de toute valeur à virgule flottante x, donc la réponse est nécessairement fausse.

je l'ai mentionné que tripartition ne tient pas pour les valeurs à virgule flottante. Cependant, il y a une propriété similaire qui ne tient. Article 5.11, paragraphe 2 de la norme 754-2008:

  

Quatre relations mutuellement exclusives sont possibles: moins, égal, supérieur, et non ordonnée. Le dernier cas survient lorsqu'au moins un des opérandes est NaN. Chaque NaN comparera avec tout non ordonnée, y compris lui-même.

En ce qui concerne l'écriture de code supplémentaire pour gérer NaN va, il est généralement possible (mais pas toujours facile) de structurer votre code de telle sorte que NaN tombent à travers correctement, mais ce n'est pas toujours le cas. Quand il n'est pas, un code supplémentaire peut être nécessaire, mais c'est un petit prix à payer pour la commodité que la fermeture algébrique porté à l'arithmétique à virgule flottante.


Addendum: De nombreux intervenants ont fait valoir qu'il serait plus utile de préserver la réflexivité de l'égalité et trichotomy au motif que l'adoption NaN! = NaN ne semble pas conserver tout axiome familier. J'avoue avoir une certaine sympathie pour ce point de vue, donc je pensais que je revenir sur cette réponse et de fournir un peu plus de contexte.

Si je comprends bien de parler à Kahan est que NAN = NaN origine de deux considérations pragmatiques:

  • Ce x == y devrait être équivalente à x - y == 0 lorsque cela est possible (au-delà d'être un théorème d'arithmétique réelle, cela rend plus efficace l'espace implémentation matérielle de comparaison, qui était d'une importance capitale au moment où la norme a été élaborée - Note, cependant, que cette violation pour x = y = infini, il est donc pas une bonne raison pour lui-même,. il aurait pu raisonnablement être plié à (x - y == 0) or (x and y are both NaN))

  • Plus important encore, il n'y avait pas de prédicats isnan( ) au moment où NaN a été officialisée dans le calcul 8087; il était nécessaire de fournir aux programmeurs un moyen pratique et efficace de détection des valeurs NaN qui ne dépendent pas des langages de programmation fournissant quelque chose comme isnan( ) qui pourrait prendre plusieurs années. Je vais citer propre écriture de Kahan sur le sujet:

  

Y avait-il pas moyen de se débarrasser de Nans, ils seraient aussi inutiles que indéfinis sur CRAY; dès qu'un ont été rencontrés, le calcul serait plus facile à arrêter plutôt que poursuivi pour une durée indéterminée à une conclusion Indéterminée. Voilà pourquoi certaines opérations sur NaN doivent fournir des résultats non-NNA. Quelles opérations? ... Les exceptions sont C prédicats « x == x » et « x! = X », qui sont respectivement 1 et 0 pour enombre très fini ou infini x mais en sens inverse si x est pas un nombre (NaN); ceux-ci constituent la seule distinction simple entre unexceptional NaN et chiffres dans les langues qui ne disposent pas d'un mot pour NaN et un prédicat IsNaN (x).

Notez que ceci est aussi la logique qui exclut le retour quelque chose comme un « non-A-booléenne ». Peut-être que ce pragmatisme a été mal placé, et la norme aurait dû exiger isnan( ), mais cela aurait fait NaN presque impossible d'utiliser de manière efficace et pratique depuis plusieurs années alors que le monde a attendu l'adoption de langage de programmation. Je ne suis pas convaincu qu'il aurait été un compromis raisonnable.

Pour être franc: le résultat de NaN == NaN ne va pas changer maintenant. Mieux vaut apprendre à vivre avec elle que de se plaindre sur Internet. Si vous voulez faire valoir qu'une relation d'ordre approprié pour les conteneurs devraient aussi existe, je recommande préconiser que votre langage de programmation préféré mettre en œuvre le prédicat totalOrder normalisé dans la norme IEEE-754 (2008). Le fait qu'il n'a pas parle déjà de la validité des préoccupations de Kahan qui a motivé l'état actuel des choses.

Autres conseils

NaN peut être considéré comme un état indéfini / nombre. similaire au concept de non définies ou étant 0/0 sqrt (-3) (dans le système réel où la vie à virgule flottante).

NaN est utilisé comme une sorte d'espace réservé pour cet état indéfini. Mathématiquement parlant, non défini ne correspond pas à pas défini. Ni pouvez-vous dire 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) à RACINE (-2). Ils renvoient toutes les deux NaN mais ils ne sont pas équivalents, même si elles retournent la même valeur. Par conséquent, l'égalité ayant toujours faux retour lorsqu'ils traitent avec NaN est le comportement souhaité.

Pour jeter encore une autre analogie. Si je vous tends deux boîtes, et vous dire qu'aucun d'entre eux contient une pomme, voulez-vous me dire que les boîtes contiennent la même chose?

NaN ne contient aucune information sur ce que quelque chose est, tout ce qu'il n'est pas. Par conséquent, ces éléments ne peuvent jamais vraiment être considérés comme égaux.

De l'article wikipedia sur NaN , les pratiques suivantes peuvent provoquer NaN:

  • Toutes les opérations mathématiques> avec un NaN au moins un opérande
  • Les divisions 0/0, ∞ / ∞, ∞ / -∞, -∞ / ∞ et -∞ / -∞
  • Les multiplications 0 × ∞ et 0 × -∞
  • Les additions ∞ + (-∞), (-∞) + ∞ et équivalents soustractions.
  • L'application d'une fonction d'arguments en dehors de son domaine, y compris en prenant la racine carrée d'un nombre négatif, en prenant le logarithme d'un nombre négatif, en prenant la tangente d'un multiple impair de 90 degrés (ou n / 2 radians), ou en prenant le sinus inverse ou cosinus d'un nombre qui est inférieur à -1 ou supérieure à 1.

Comme il n'y a aucun moyen de savoir lequel de ces opérations a créé le NaN, il n'y a aucun moyen de les comparer ce sens.

Je ne sais pas la raison d'être de la conception, mais voici un extrait de la norme IEEE 754-1985:

.

"Il est possible de comparer les chiffres à virgule flottante dans tous les formats pris en charge, même si les formats de opérandes diffèrent Les comparaisons sont exactes et ne débordent ni soupassement Quatre relations mutuellement exclusives sont possibles:. Inférieur, égal, supérieur et non ordonnée. Le dernier cas survient lorsqu'au moins un des opérandes est NaN. Chaque NaN comparera avec tout non ordonnée, y compris lui-même. "

Il semble que particulier parce que la plupart des environnements de programmation qui permettent NaN ne permettent pas aussi logique 3-évalué. Si vous jetez une logique à 3 valeurs dans le mélange, il devient cohérent:

  • (2,7 == 2,7) = true
  • (2,7 == 2,6) = false
  • (2,7 == NaN) = inconnu
  • (NaN == NaN) = inconnu

Même .NET ne fournit pas un opérateur de bool? operator==(double v1, double v2), de sorte que vous êtes toujours coincé avec le résultat (NaN == NaN) = false stupide.

Je devine que NaN (pas un nombre) signifie exactement que:. Ce n'est pas un numéro et donc comparer cela ne fait pas vraiment de sens

Il est un peu comme l'arithmétique dans SQL avec null opérandes: Ils résultat dans null

.

Les comparaisons pour les nombres à virgule flottante comparer des valeurs numériques. Ainsi, ils ne peuvent pas être utilisés pour les valeurs non numériques. donc NaN ne peut être comparé dans un sens numérique.

La réponse simpliste est qu'un NaN n'a pas de valeur numérique, donc il n'y a rien dans pour comparer à quoi que ce soit d'autre.

Vous pouvez envisager des tests et le remplacement de vos NaN avec + INF si vous voulez agir comme + INF.

NaN est une nouvelle instance implicite (d'un type particulier d'erreur d'exécution). Cela signifie NaN !== NaN pour la même raison que new Error !== new Error;

Et gardez à l'esprit comme implicitness se voit aussi des erreurs en dehors, par exemple dans le contexte des expressions régulières, cela signifie /a/ !== /a/ qui est le sucre juste syntaxe pour new RegExp('a') !== new RegExp('a')

Alors que je suis d'accord que les comparaisons de NaN avec un nombre réel devrait être non ordonnée, je pense qu'il ya un motif valable de comparer NaN avec lui-même. Comment, par exemple, on ne découvre la différence entre signalisation et NaN NaN calme? Si nous pensons aux signaux comme un ensemble de valeurs booléennes on pourrait bien (à savoir un peu vecteur) demander si les vecteurs de bits sont identiques ou différents et commander les jeux en conséquence. Par exemple, le décodage d'un exposant biaisé au maximum, si le significand était de gauche décalée vers de façon à aligner le bit le plus significatif de la mantisse sur le bit le plus significatif du format binaire, une valeur négative serait un NaN calme et une valeur positive serait être un NaN de signalisation. Zéro est bien sûr réservé à l'infini et la comparaison serait non ordonnée. alignement MSB permettrait la comparaison directe des signaux même de différents formats binaires. Deux NaN avec le même ensemble de signaux serait donc équivalent et donner un sens à l'égalité.

Parce que les mathématiques est le domaine où les nombres « existent juste ». Dans vous devez initialize ces chiffres et garder leur état en fonction de vos besoins. A ces vieux jours initialisation de la mémoire a travaillé dans les façons dont vous ne pourriez jamais se fier. Vous ne pouvez vous permettre de penser à ce « oh, ce serait initialisés avec 0xCD tout le temps, mon algo sera pas cassé » .

Vous devez donc bon non mélange solvant assez collant pour ne pas laisser votre algorithme de se faire sucer dans et cassé. De bons algorithmes impliquant des nombres vont surtout travailler avec les relations et les if () relations seront omis.

Ceci est juste la graisse que vous pouvez mettre en nouvelle variable à la création, au lieu de programmer l'enfer au hasard de la mémoire de l'ordinateur. Et votre algorithme quoi que ce soit, ne se cassera pas.

Ensuite, lorsque vous encore trouver tout à coup que votre algorithme produit NaN, il est possible de le nettoyer, en regardant dans chaque branche une à la fois. Encore une fois, la règle « toujours faux » aide beaucoup dans ce domaine.

Pour moi, la façon de l'expliquer est plus facile:

  

J'ai quelque chose et si ce n'est pas une pomme est-il alors une orange?

Vous ne pouvez pas comparer NaN avec autre chose (même lui-même) parce qu'il n'a pas de valeur. En outre, il peut être une valeur (à l'exception d'un numéro).

  

J'ai quelque chose et si elle ne correspond pas à un nombre est alors une chaîne?

Très courte réponse:

Parce que les éléments suivants: nan / nan = 1 ne doit pas détenir. Dans le cas contraire inf/inf serait 1.

(Par conséquent nan ne peut pas être égale à nan. Pour ce qui est > ou <, si nan respecterait une relation d'ordre dans un ensemble satisfaisant la archimédien, nous avons à nouveau nan / nan = 1 à la limite).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top