En Java, que signifie NaN?


107

J'ai un programme qui essaie de réduire un doublenombre souhaité. Le résultat que j'obtiens est NaN.

Que NaNsignifie Java?


Il existe une bonne description de NaN et des pièges courants lors de l'utilisation de NaN en Java: ppkwok.blogspot.co.uk/2012/11/…
Phil

Si vous demandez "à quoi sert NaN?" en Java (ou dans tout autre langage), je peux vous donner un cas d'utilisation où c'est très pratique: quand j'ai un tableau 2D de flottants, mais que mon calcul n'a pas de valeur significative pour une partie de ce tableau 2D, Je vais remplir cette valeur avec "NaN". Cela peut être utilisé pour signaler aux utilisateurs en aval de mon calcul (comme quand il est transformé en une image raster) "ne faites pas attention à la valeur à ce stade". Très utile!
Dan H

BTW, que signifie - exactement - «rétrécir» un double? Curious ...
Dan H

Réponses:


153

Tiré de cette page :

"NaN" signifie "pas un nombre". "Nan" est produit si une opération en virgule flottante a des paramètres d'entrée qui amènent l'opération à produire un résultat non défini. Par exemple, 0,0 divisé par 0,0 est arithmétiquement indéfini. Prendre la racine carrée d'un nombre négatif est également indéfini.


16
De plus, NaN est défini de manière assez explicite par la norme IEEE pour l'arithmétique à virgule flottante (IEEE 754) que Java suit aveuglément. La lecture de la norme vous ouvre les yeux sur beaucoup de choses, les multiples valeurs de zéro étant l'une des choses.
Esko

37
En outre, NaNa la propriété intéressante d'être le seul "nombre" qui n'est pas le même que lui-même une fois comparé. Par conséquent , une commune (et dans de nombreuses langues le seul) test si un nombre xest NaNle suivant:boolean isNaN(x){return x != x;}
quazgar

3
Le lien en réponse est mort?
Pang

3
... "Prendre la racine carrée d'un nombre négatif n'est pas défini (en arithmétique)" ... Ce n'est pas le cas! c'est en fait iet certains langages comme python le traitent très bien ... Ce n'est peut-être pas le cas chez javatoi
Rafael T

5
@RafaelT Je dirais qu'il n'est pas défini en arithmétique non complexe. Il n'y a aucun moyen d'attribuer un nombre complexe à un flottant ou un double en Java. Python est typé dynamiquement, il peut donc être possible de simplement renvoyer un nombre complexe dans ce cas.
sstn

19

NaNsignifie «Pas un nombre» et est essentiellement une représentation d'une valeur à virgule flottante spéciale dans la norme à virgule flottante IEE 754 . NaN signifie généralement que la valeur est quelque chose qui ne peut pas être exprimé avec un nombre à virgule flottante valide.

Une conversion aboutira à cette valeur, lorsque la valeur à convertir est autre chose, par exemple lors de la conversion d'une chaîne qui ne représente pas un nombre.


Comment convertir? Avec parseFloat()ou parseDouble? Ou autre chose?
Alonso del Arte

14

NaNsignifie "Pas un nombre" et est le résultat d'opérations non définies sur les nombres à virgule flottante comme par exemple la division de zéro par zéro. (Notez que si la division d'un nombre non nul par zéro est également généralement indéfinie en mathématiques, cela ne se traduit pas par NaN mais par l'infini positif ou négatif).


5

NaNsignifie «Pas un nombre». C'est une valeur à virgule flottante spéciale qui signifie que le résultat d'une opération n'a pas été défini ou ne peut pas être représenté en tant que nombre réel.

Voir ici pour plus d'explications sur cette valeur.




4

Cela ne signifie pas un nombre. C'est une représentation courante d'une valeur numérique impossible dans de nombreux langages de programmation.


4

Exemple exécutable minimal

La première chose que vous devez savoir, c'est que le concept de NaN est implémenté directement sur le matériel du CPU.

Tous les principaux processeurs modernes semblent suivre IEEE 754 qui spécifie les formats à virgule flottante, et les NaN, qui ne sont que des valeurs flottantes spéciales, font partie de cette norme.

Par conséquent, le concept sera très similaire dans tous les langages, y compris Java qui émet simplement du code en virgule flottante directement vers le processeur.

Avant de continuer, vous voudrez peut-être lire d'abord les réponses suivantes que j'ai écrites:

Maintenant, pour une action Java. La plupart des fonctions d'intérêt qui ne sont pas dans le langage de base vivent à l'intérieur java.lang.Float.

Nan.java

import java.lang.Float;
import java.lang.Math;

public class Nan {
    public static void main(String[] args) {
        // Generate some NaNs.
        float nan            = Float.NaN;
        float zero_div_zero  = 0.0f / 0.0f;
        float sqrt_negative  = (float)Math.sqrt(-1.0);
        float log_negative   = (float)Math.log(-1.0);
        float inf_minus_inf  = Float.POSITIVE_INFINITY - Float.POSITIVE_INFINITY;
        float inf_times_zero = Float.POSITIVE_INFINITY * 0.0f;
        float quiet_nan1     = Float.intBitsToFloat(0x7fc00001);
        float quiet_nan2     = Float.intBitsToFloat(0x7fc00002);
        float signaling_nan1 = Float.intBitsToFloat(0x7fa00001);
        float signaling_nan2 = Float.intBitsToFloat(0x7fa00002);
        float nan_minus      = -nan;

        // Generate some infinities.
        float positive_inf   = Float.POSITIVE_INFINITY;
        float negative_inf   = Float.NEGATIVE_INFINITY;
        float one_div_zero   = 1.0f / 0.0f;
        float log_zero       = (float)Math.log(0.0);

        // Double check that they are actually NaNs.
        assert  Float.isNaN(nan);
        assert  Float.isNaN(zero_div_zero);
        assert  Float.isNaN(sqrt_negative);
        assert  Float.isNaN(inf_minus_inf);
        assert  Float.isNaN(inf_times_zero);
        assert  Float.isNaN(quiet_nan1);
        assert  Float.isNaN(quiet_nan2);
        assert  Float.isNaN(signaling_nan1);
        assert  Float.isNaN(signaling_nan2);
        assert  Float.isNaN(nan_minus);
        assert  Float.isNaN(log_negative);

        // Double check that they are infinities.
        assert  Float.isInfinite(positive_inf);
        assert  Float.isInfinite(negative_inf);
        assert !Float.isNaN(positive_inf);
        assert !Float.isNaN(negative_inf);
        assert one_div_zero == positive_inf;
        assert log_zero == negative_inf;
            // Double check infinities.

        // See what they look like.
        System.out.printf("nan            0x%08x %f\n", Float.floatToRawIntBits(nan           ), nan           );
        System.out.printf("zero_div_zero  0x%08x %f\n", Float.floatToRawIntBits(zero_div_zero ), zero_div_zero );
        System.out.printf("sqrt_negative  0x%08x %f\n", Float.floatToRawIntBits(sqrt_negative ), sqrt_negative );
        System.out.printf("log_negative   0x%08x %f\n", Float.floatToRawIntBits(log_negative  ), log_negative  );
        System.out.printf("inf_minus_inf  0x%08x %f\n", Float.floatToRawIntBits(inf_minus_inf ), inf_minus_inf );
        System.out.printf("inf_times_zero 0x%08x %f\n", Float.floatToRawIntBits(inf_times_zero), inf_times_zero);
        System.out.printf("quiet_nan1     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan1    ), quiet_nan1    );
        System.out.printf("quiet_nan2     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan2    ), quiet_nan2    );
        System.out.printf("signaling_nan1 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan1), signaling_nan1);
        System.out.printf("signaling_nan2 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan2), signaling_nan2);
        System.out.printf("nan_minus      0x%08x %f\n", Float.floatToRawIntBits(nan_minus     ), nan_minus     );
        System.out.printf("positive_inf   0x%08x %f\n", Float.floatToRawIntBits(positive_inf  ), positive_inf  );
        System.out.printf("negative_inf   0x%08x %f\n", Float.floatToRawIntBits(negative_inf  ), negative_inf  );
        System.out.printf("one_div_zero   0x%08x %f\n", Float.floatToRawIntBits(one_div_zero  ), one_div_zero  );
        System.out.printf("log_zero       0x%08x %f\n", Float.floatToRawIntBits(log_zero      ), log_zero      );

        // NaN comparisons always fail.
        // Therefore, all tests that we will do afterwards will be just isNaN.
        assert !(1.0f < nan);
        assert !(1.0f == nan);
        assert !(1.0f > nan);
        assert !(nan == nan);

        // NaN propagate through most operations.
        assert Float.isNaN(nan + 1.0f);
        assert Float.isNaN(1.0f + nan);
        assert Float.isNaN(nan + nan);
        assert Float.isNaN(nan / 1.0f);
        assert Float.isNaN(1.0f / nan);
        assert Float.isNaN((float)Math.sqrt((double)nan));
    }
}

GitHub en amont .

Courir avec:

javac Nan.java && java -ea Nan

Production:

nan            0x7fc00000 NaN
zero_div_zero  0x7fc00000 NaN
sqrt_negative  0xffc00000 NaN
log_negative   0xffc00000 NaN
inf_minus_inf  0x7fc00000 NaN
inf_times_zero 0x7fc00000 NaN
quiet_nan1     0x7fc00001 NaN
quiet_nan2     0x7fc00002 NaN
signaling_nan1 0x7fa00001 NaN
signaling_nan2 0x7fa00002 NaN
nan_minus      0xffc00000 NaN
positive_inf   0x7f800000 Infinity
negative_inf   0xff800000 -Infinity
one_div_zero   0x7f800000 Infinity
log_zero       0xff800000 -Infinity

Nous apprenons donc quelques choses:

  • des opérations flottantes étranges qui n'ont aucun résultat raisonnable donnent NaN:

    • 0.0f / 0.0f
    • sqrt(-1.0f)
    • log(-1.0f)

    générer un NaN.

    En C, il est en fait possible de demander des signaux à lever sur de telles opérations avec feenableexceptpour les détecter, mais je ne pense pas que cela soit exposé en Java: Pourquoi la division entière par zéro 1/0 donne-t-elle une erreur mais une virgule flottante 1 / 0,0 renvoie "Inf"?

  • les opérations étranges qui sont à la limite de l'infini plus ou moins donnent cependant + - infini au lieu de NaN

    • 1.0f / 0.0f
    • log(0.0f)

    0.0 tombe presque dans cette catégorie, mais le problème est probablement qu'il pourrait aller à plus ou moins l'infini, donc il a été laissé comme NaN.

  • si NaN est l'entrée d'une opération flottante, la sortie a également tendance à être NaN

  • il y a plusieurs valeurs possibles pour NaN 0x7fc00000, 0x7fc00001, 0x7fc00002, bien que x86_64 semble générer seulement 0x7fc00000.

  • NaN et l'infini ont une représentation binaire similaire.

    Décomposons-en quelques-uns:

    nan          = 0x7fc00000 = 0 11111111 10000000000000000000000
    positive_inf = 0x7f800000 = 0 11111111 00000000000000000000000
    negative_inf = 0xff800000 = 1 11111111 00000000000000000000000
                                | |        |
                                | |        mantissa
                                | exponent
                                |
                                sign

    À partir de là, nous confirmons ce que IEEE754 spécifie:

    • NaN et les infinis ont l'exposant == 255 (tous les uns)
    • les infinis ont la mantisse == 0. Il n'y a donc que deux infinis possibles: + et -, différenciés par le bit de signe
    • NaN a la mantisse! = 0. Il y a donc plusieurs possibilités, sauf pour la mantisse == 0 qui est l'infini
  • Les NaN peuvent être positifs ou négatifs (bit du haut), bien que cela n'ait aucun effet sur les opérations normales

Testé dans Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.


3

Pas un gars de Java, mais dans JS et d'autres langages, j'utilise "Pas un nombre", ce qui signifie qu'une opération a fait en sorte qu'il ne devienne pas un nombre valide.


3

Cela signifie littéralement «Pas un nombre». Je soupçonne que quelque chose ne va pas avec votre processus de conversion.

Consultez la section Not A Number à cette référence


3

Pas une valeur à virgule flottante valide (par exemple, le résultat de la division par zéro)

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


Je chipote avec cette réponse. Premièrement: "NaN" EST une valeur valide pour un float IEEE! (Après tout, il est défini dans la spécification ... donc c'est "valide", non?). Deuxièmement: la "division par zéro" peut être représentée par IEEE "Positive Infinity" ou "Negative Infinity"; un meilleur exemple de "NaN" est "zéro divisé par zéro", comme certaines autres réponses l'ont correctement souligné.
Dan H

"Valeur valide" et "définie dans la spécification" n'est pas la même chose. Accepté 0/0.
Vladimir Dyuzhev
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.