157 votes

Pourquoi Double.NaN==Double.NaN renvoie-t-il faux ?

J'étais en train d'étudier les questions OCPJP et j'ai trouvé ce code étrange :

public static void main(String a[]) {
    System.out.println(Double.NaN==Double.NaN);
    System.out.println(Double.NaN!=Double.NaN);
}

Quand j'ai exécuté le code, j'ai obtenu :

false
true

Comment est la sortie false quand on compare deux choses qui se ressemblent ? Que fait NaN C'est-à-dire ?

8 votes

C'est vraiment bizarre. Parce que Double.NaN est static final, la comparaison avec == devrait retourner vrai. +1 pour la question.

2 votes

Il en va de même en python : In [1]: NaN==NaN Out[1]: False

58 votes

Il en va de même dans toutes les langues qui suivent correctement la norme IEEE 754.

139voto

Adrian Mitev Points 2515

NaN signifie "Not a Number" (pas un nombre).

Spécification du langage Java (JLS) Troisième édition dit :

Une opération qui déborde produit un infini signé, une opération qui ne déborde pas produit une valeur dénormalisée ou un zéro signé, et une opération qui n'a pas de résultat mathématiquement défini produit NaN. Toutes les opérations numériques avec NaN comme opérande produisent NaN comme résultat. Comme cela a déjà été décrit, NaN n'est pas ordonné, donc une opération de comparaison numérique impliquant un ou deux NaN renvoie false et tout != comparaison impliquant des retours NaN true dont x!=x quand x est NaN.

4 votes

@nibot : En grande partie vrai . Toute comparaison avec un flotteur conforme à l'IEEE produira false . Cette norme diffère donc de Java en ce sens que l'IEEE exige que (NAN != NAN) == false .

3 votes

En ouvrant cette boîte de Pandore, où voyez-vous que "l'IEEE exige que (NAN != NAN) == faux" ?

62voto

Peter Lawrey Points 229686

Par définition, NaN n'est égal à aucun nombre, y compris NaN. Cela fait partie de la norme IEEE 754 et est mis en œuvre par le CPU/FPU. La JVM n'a pas à ajouter de logique pour le supporter.

http://en.wikipedia.org/wiki/NaN

Une comparaison avec un NaN renvoie toujours un résultat non ordonné, même lorsqu'elle est comparée avec elle-même. ... Les prédicats d'égalité et d'inégalité ne signalent rien, de sorte que x = x retournant false peut être utilisé pour tester si x est un NaN silencieux.

Java traite tous les NaN comme des NaN silencieux.

1 votes

Est-il implémenté par le CPU, ou est-il câblé dans la JVM comme le mentionne Bohemian ?

3 votes

La JVM doit appeler ce qui l'implémente correctement. Sur un PC, le CPU fait tout le travail en tant que tel. Sur une machine sans ce support, la JVM doit l'implémenter. (Je ne connais pas de machine de ce type).

0 votes

A l'époque où le 8087 était une option, la bibliothèque C contenait un émulateur FP. Les programmes comme la JVM n'avaient pas à s'en soucier de toute façon.

51voto

falsarella Points 2674

Pourquoi cette logique

NaN signifie Not a Number . Qu'est-ce qui n'est pas un nombre ? N'importe quoi. Vous pouvez avoir n'importe quoi dans un côté et n'importe quoi dans l'autre, donc rien ne garantit que les deux sont égaux. NaN est calculé avec Double.longBitsToDouble(0x7ff8000000000000L) et comme vous pouvez le voir dans la documentation de longBitsToDouble :

Si l'argument est une valeur quelconque dans la plage 0x7ff0000000000001L par le biais de 0x7fffffffffffffffL ou dans l'intervalle 0xfff0000000000001L par le biais de 0xffffffffffffffffL le résultat est un NaN .

Aussi, NaN est traitée logiquement dans l'API.


Documentation

/** 
 * A constant holding a Not-a-Number (NaN) value of type
 * {@code double}. It is equivalent to the value returned by
 * {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
 */
public static final double NaN = 0.0d / 0.0;

Au fait, NaN est testé comme votre échantillon de code :

/**
 * Returns {@code true} if the specified number is a
 * Not-a-Number (NaN) value, {@code false} otherwise.
 *
 * @param   v   the value to be tested.
 * @return  {@code true} if the value of the argument is NaN;
 *          {@code false} otherwise.
 */
static public boolean isNaN(double v) {
    return (v != v);
}

Solution

Ce que vous pouvez faire, c'est utiliser compare / compareTo :

Double.NaN est considéré par cette méthode comme étant égal à lui-même et supérieur à tous les autres double (y compris Double.POSITIVE_INFINITY ).

Double.compare(Double.NaN, Double.NaN);
Double.NaN.compareTo(Double.NaN);

Ou, equals :

Si this et argument les deux représentent Double.NaN alors le site equals La méthode retourne true même si Double.NaN==Double.NaN a la valeur false .

Double.NaN.equals(Double.NaN);

0 votes

Connaissez-vous un cas où avoir NaN != NaN être faux rendrait les programmes plus compliqués qu'en ayant NaN != NaN être vrai ? Je sais que IEEE a pris cette décision il y a des années, mais d'un point de vue pratique, je n'ai jamais vu de cas où cela est utile. Si une opération est censée se dérouler jusqu'à ce que des itérations consécutives donnent le même résultat, le fait que deux itérations consécutives donnent NaN serait "naturellement" détecté comme une condition de sortie si ce n'était pas ce comportement.

0 votes

@supercat Comment pouvez-vous dire que deux non nombres aléatoires sont naturellement égaux ? Ou disons, primitivement égaux ? Pensez à NaN comme une instance, pas comme quelque chose de primitif. Chaque résultat anormal différent est une instance différente de quelque chose d'étrange et même si les deux devraient représenter la même chose, l'utilisation de == pour des instances différentes doit retourner false. D'un autre côté, lorsque vous utilisez ==, cela peut être géré correctement comme vous le souhaitez. [ docs.oracle.com/javase/7/docs/api/java/lang/

0 votes

@falsarella : La question n'est pas de savoir si deux nombres aléatoires doivent être considérés comme "définitivement égaux", mais plutôt dans quels cas il est utile qu'un nombre quelconque soit comparé comme "définitivement inégal" à lui-même. Si l'on essaie de calculer la limite de f(f(f...f(x))) et on trouve un y=f[n](x) pour certains n de sorte que le résultat de f(y) est indiscernable de y alors y sera indiscernable du résultat d'une opération plus profondément imbriquée. f(f(f(...f(y))) . Même si on voulait NaN==NaN d'être faux, ayant Nan!=Nan également être fausse serait moins "surprenante" que d'avoir x!=x être vrai pour un certain x.

6voto

Bohemian Points 134107

Le site javadoc pour Double.NaN dit tout :

Une constante contenant une valeur Not-a-Number (NaN) de type double . Elle est équivalente à la valeur renvoyée par Double.longBitsToDouble(0x7ff8000000000000L) .

Il est intéressant de noter que la source de Double définit NaN ainsi :

public static final double NaN = 0.0d / 0.0;

Le comportement spécial que vous décrivez est câblé dans la JVM.

6 votes

Est-ce câblé dans la JVM, ou est-ce implémenté par le CPU comme le mentionne Peter ?

3voto

larsmans Points 167484

NaN est une valeur spéciale qui signifie "pas un nombre" ; c'est le résultat de certaines opérations arithmétiques invalides, telles que sqrt(-1) et possède la propriété (parfois gênante) suivante NaN != NaN .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X