Java'da NaN ne anlama geliyor?


107

A'yı doubleistenen sayıya indirmeye çalışan bir programım var . Aldığım çıktı NaN.

NaNJava'da ne anlama geliyor?


Java'da NaN kullanırken NaN'nin
Phil

"NaN ne işe yarar?" Diye soruyorsanız Java'da (veya başka bir dilde), size çok kullanışlı olduğu bir kullanım durumu verebilirim: 2 boyutlu bir kayan nokta dizisine sahip olduğumda, ancak hesaplamamın bu 2 boyutlu dizinin bir kısmı için anlamlı bir değeri olmadığında, Bu değeri "NaN" ile dolduracağım. Bu, hesaplamamın alt kullanıcılarına sinyal vermek için kullanılabilir (örneğin, bir raster görüntüye dönüştürüldüğünde) "bu noktada değere dikkat etmeyin". Çok kullanışlı!
Dan H

BTW, tam olarak - bir çift "küçültmek" anlamına mı geliyor? Meraklı ...
Dan H

Yanıtlar:


153

Alındığı bu sayfada :

"NaN", "sayı değil" anlamına gelir. Bir kayan noktalı işlem, işlemin bazı tanımlanmamış sonuçlar üretmesine neden olan bazı girdi parametrelerine sahipse "Nan" üretilir. Örneğin, 0.0'ın 0.0'a bölünmesi aritmetik olarak tanımsızdır. Negatif bir sayının karekökünü almak da tanımsızdır.


16
Ek olarak, NaN, Java'nın körü körüne takip ettiği Kayan Nokta Aritmetiği için IEEE Standardı (IEEE 754) tarafından oldukça açık bir şekilde tanımlanır. Standardı okumak, gözlerinizi birçok şeye açar, sıfırın çoklu değerleri şeylerden biridir.
Esko

37
Ayrıca, NaNkıyaslandığında kendisiyle aynı olmayan tek "sayı" olma özelliği de ilginçtir. Bu nedenle (ve birçok dilde okunur) bir sayı ise testi yaygın xolan NaNşudur:boolean isNaN(x){return x != x;}
quazgar

3
Cevapta bağlantı öldü mü?
Pang

3
... "Negatif sayının karekökünü almak tanımsızdır (aritmetikte)" ... Değil! onun aslında ive çok iyi onunla piton anlaşma gibi bazı diller ... O değil durumda olabilir javathou
Rafael T

5
@RafaelT Karmaşık olmayan aritmetikte tanımsız olduğunu söyleyebilirim. Java'da bir float veya double'a karmaşık bir sayı atamanın bir yolu yoktur. Python dinamik olarak yazılmıştır, bu nedenle bu durumda sadece karmaşık bir sayı döndürmek mümkün olabilir.
sstn

19

NaN"Sayı Değil" anlamına gelir ve temelde IEE 754 kayan nokta standardında özel bir kayan nokta değerinin bir temsilidir . NaN genellikle değerin geçerli bir kayan nokta sayısıyla ifade edilemeyen bir şey olduğu anlamına gelir.

Bir dönüşüm, dönüştürülen değer başka bir şey olduğunda, örneğin bir sayıyı temsil etmeyen bir dizeyi dönüştürürken bu değerle sonuçlanacaktır.


Nasıl dönüştürülüyor? İle parseFloat()veya parseDouble? Veya başka bir şey?
Alonso del Arte

14

NaN"Sayı Değil" anlamına gelir ve örneğin sıfırı sıfıra bölmek gibi kayan noktalı sayılar üzerindeki tanımsız işlemlerin sonucudur. (Sıfır olmayan bir sayıyı sıfıra bölmenin matematikte genellikle tanımsız olmasına rağmen, NaN ile sonuçlanmadığını, pozitif veya negatif sonsuzluk ile sonuçlandığını unutmayın).


5

NaN"Sayı değil" anlamına gelir. Bu, bir işlemin sonucunun tanımlanmadığı veya gerçek bir sayı olarak gösterilemediği anlamına gelen özel bir kayan nokta değeridir.

Bu değerin daha fazla açıklaması için buraya bakın .




4

Sayı Değil Anlamına Gelir. Birçok programlama dilinde imkansız bir sayısal değerin ortak bir temsilidir.


4

Minimum çalıştırılabilir örnek

Bilmeniz gereken ilk şey, NaN kavramının doğrudan CPU donanımına uygulanmış olmasıdır.

Tüm büyük modern CPU'lar , kayan nokta formatlarını belirten IEEE 754'ü takip ediyor gibi görünmektedir ve sadece özel kayan değer olan NaN'ler bu standardın parçasıdır.

Bu nedenle, kavram, kayan nokta kodunu doğrudan CPU'ya gönderen Java da dahil olmak üzere herhangi bir dilde çok benzer olacaktır.

Devam etmeden önce, yazdığım aşağıdaki cevapları okumak isteyebilirsiniz:

Şimdi biraz Java eylemi için. Çekirdek dilde olmayan ilgi işlevlerinin çoğu içeride yaşar 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 yukarı akış .

Şununla çalıştırın:

javac Nan.java && java -ea Nan

Çıktı:

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

Bundan birkaç şey öğreniyoruz:

  • mantıklı bir sonucu olmayan garip kayan işlemler NaN verir:

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

    bir NaN.

    C'de, bu tür işlemlerde feenableexceptonları algılamak için sinyallerin yükseltilmesini istemek aslında mümkündür , ancak bunun Java'da ortaya çıktığını düşünmüyorum: Tamsayı 1 / 0'a bölme neden hata veriyor ama kayan nokta 1 / 0.0 "Inf" döndürür mü?

  • artı veya eksi sonsuzluk sınırında olan garip işlemler, ancak NaN yerine + - sonsuzluk verir

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

    0.0 Neredeyse bu kategoriye girer, ancak büyük olasılıkla sorun, artı ya da eksi sonsuza gidebilmesi, dolayısıyla NaN olarak kalmasıdır.

  • NaN, kayan bir işlemin girdisi ise, çıktı da NaN olma eğilimindedir

  • Birkaç olası değerler NaN için vardır 0x7fc00000, 0x7fc00001, 0x7fc00002, x86_64 sadece üretmek için görünse de 0x7fc00000.

  • NaN ve sonsuz benzer ikili gösterime sahiptir.

    Birkaç tanesini inceleyelim:

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

    Buradan IEEE754'ün neyi belirttiğini onaylıyoruz:

    • hem NaN hem de sonsuzlar üslü == 255 (tümü)
    • sonsuzlukların mantisi == 0. Bu nedenle sadece iki olası sonsuzluk vardır: + ve -, işaret biti ile farklılaştırılmış
    • NaN'nin mantisi! = 0. Bu nedenle sonsuzluk olan mantis == 0 dışında birkaç olasılık vardır.
  • NaN'ler pozitif veya negatif (üst bit) olabilir, ancak bunun normal işlemler üzerinde bir etkisi yoktur.

Ubuntu 18.10 amd64, OpenJDK 1.8.0_191'de test edilmiştir.


3

Java görevlisi değil, JS ve diğer dillerde "Sayı Değil" kullanıyorum, yani bazı işlemler onun geçerli bir sayı olmamasına neden oldu.


3

Kelimenin tam anlamıyla "Sayı Değil" anlamına gelir. Dönüşüm sürecinizde bir sorun olduğundan şüpheleniyorum.

Bu referanstaki Not A Number bölümüne göz atın


3

Geçerli bir kayan nokta değeri değil (ör. Sıfıra bölmenin sonucu)

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


Bu cevapla tartışıyorum. İlk olarak: "NaN", bir IEEE kayan nokta için geçerli bir değerdir! (Sonuçta, spesifikasyonda tanımlanmıştır ... yani "geçerli", değil mi?). İkincisi: "sıfıra bölme", ​​IEEE "Pozitif Sonsuzluk" veya "Negatif Sonsuzluk" ile temsil edilebilir; "NaN" için daha iyi bir örnek, diğer bazı cevapların doğru bir şekilde işaret ettiği gibi "sıfır bölü sıfır" dır.
Dan H

"Geçerli değer" ve "spesifikasyonda tanımlanan" aynı şey değildir. 0/0 ile kabul edildi.
Vladimir Dyuzhev
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.