web-dev-qa-db-fra.com

En Java, que signifie NaN?

J'ai un programme qui essaie de réduire une double au nombre souhaité. Le résultat obtenu est NaN.

Que signifie NaN en Java?

96
David

Tiré de cette page :

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

141
KennyDeriemaeker

NaN signifie "Pas un nombre" et est essentiellement une représentation d'une valeur en virgule flottante spéciale dans virgule flottante IEE 754). standard. 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 en cours de conversion est différente, par exemple lors de la conversion d'une chaîne qui ne représente pas un nombre.

18
poke

NaN signifie "Pas un nombre" et est le résultat d'opérations non définies sur des nombres en virgule flottante, comme par exemple la division de zéro par zéro. (Notez que bien que diviser un nombre non nul par zéro soit aussi généralement non défini en mathématiques, cela ne donne pas NaN mais un infini positif ou négatif).

12
sepp2k

NaN signifie "Pas un nombre." C'est une valeur spéciale en virgule flottante qui signifie que le résultat d'une opération n'a pas été défini ou ne peut pas être représenté sous forme de nombre réel.

Voir ici pour plus d'explications sur cette valeur.

5
Mike Daniels

NaN signifie pas un nombre. Il est utilisé pour signifier toute valeur mathématiquement non définie. Comme divisant 0.0 par 0.0. Vous pouvez regarder ici pour plus d'informations: https://web.archive.org/web/20120819091816/http://www.concentric.net/~ttwang/tech/javafloat.htm

Postez votre programme ici si vous avez besoin de plus d'aide.

5
Prachi

NaN = Pas un nombre.

4
Fitzchak Yitzchaki

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

4
lbedogni

Ce n'est pas un gars Java, mais dans JS et d'autres langages, j'utilise plutôt "Pas un nombre", ce qui signifie qu'une opération l'a rendu non valide.

3
Brian Mains

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

Consultez la section Pas un nombre à cette référence

3
Chris Thompson

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

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

3
Vladimir Dyuzhev

Exemple minimal exécutable

La première chose que vous devez savoir, c'est que le concept de NaN est implémenté directement sur le matériel de la 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 à virgule flottante directement vers la CPU.

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

Passons maintenant à l'action Java. La plupart des fonctions d'intérêt qui ne sont pas dans la langue principale résident à l'intérieur de 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
_

Sortie:

_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
_

Donc, à partir de cela, nous apprenons quelques choses:

  • d'étranges opérations flottantes 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 effectivement possible de demander que des signaux soient générés sur de telles opérations avec feenableexcept pour 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 une erreur mais la virgule flottante 1/0.0 renvoie "Inf"?

  • opérations étranges qui sont à la limite de plus ou moins l'infini donnent cependant + - l'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 que cela 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 tend également à être NaN

  • il existe plusieurs valeurs possibles pour NaN _0x7fc00000_, _0x7fc00001_, _0x7fc00002_, bien que x86_64 semble générer uniquement _0x7fc00000_.

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

    Décomposons quelques-uns d'entre eux:

    _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 un exposant == 255 (tous les uns)
    • les infinis ont une mantisse == 0. Il n'y a donc que deux infinis possibles: + et -, différenciés par le bit de signe
    • NaN a mantissa! = 0. Il y a donc plusieurs possibilités, sauf pour mantissa == 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é sous Ubuntu 18.10 AMD64, OpenJDK 1.8.0_191.