Mengapa Double.NaN == Double.NaN menghasilkan false?

155

Saya baru saja mempelajari pertanyaan OCPJP dan saya menemukan kode aneh ini:

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

Ketika saya menjalankan kode, saya mendapat:

false
true

Bagaimana hasilnya falseketika kita membandingkan dua hal yang terlihat sama satu sama lain? Apa NaNartinya

Maverick
sumber
8
Ini sangat aneh. Karena Double.NaN adalah final statis, perbandingan dengan == harus mengembalikan true. +1 untuk pertanyaan.
Stephan
2
Hal yang sama berlaku dalam python:In [1]: NaN==NaN Out[1]: False
tdc
58
Hal yang sama berlaku di semua bahasa yang mengikuti standar IEEE 754 dengan benar.
zzzzBov
4
Intuisi: "Halo" bukan angka, true (boolean) juga bukan angka. NaN! = NaN untuk alasan yang sama "Halo"! = Benar
Kevin
3
@Stephan: Perbandingan dengan Double.NaN==Double.NaNmemang harus mengembalikan true jika Double.NaNbertipe java.lang.Double. Namun, tipenya adalah primitif double, dan aturan operator untuk doublediterapkan (yang menuntut ketidaksetaraan ini untuk kesesuaian dengan IEEE 754, seperti yang dijelaskan dalam jawaban).
sleske

Jawaban:

139

NaN berarti "Bukan Angka".

Spesifikasi Bahasa Jawa (JLS) Edisi Ketiga mengatakan :

Operasi yang meluap menghasilkan infinity yang ditandatangani, operasi yang melimpah menghasilkan nilai yang dinormalisasi atau nol yang ditandatangani, dan operasi yang tidak memiliki hasil pasti secara matematis menghasilkan NaN. Semua operasi numerik dengan NaN sebagai operan menghasilkan NaN sebagai hasilnya. Seperti yang telah dijelaskan, NaN tidak berurutan, sehingga operasi perbandingan numerik yang melibatkan satu atau dua pengembalian NaN falsedan !=perbandingan apa pun yang melibatkan pengembalian NaN true, termasuk x!=xkapan xNaN.

Adrian Mitev
sumber
4
@nibot: Kebanyakan benar . Setiap perbandingan dengan pelampung yang sesuai dengan IEEE akan menghasilkan false. Sehingga standar berbeda dari Jawa dalam IEEE menuntut itu (NAN != NAN) == false.
Drew Dormann
2
Membuka kotak Pandora ini - di mana Anda melihat bahwa "IEEE menuntut itu (NAN! = NAN) == false"?
Supervisor
62

NaN secara definisi tidak sama dengan angka apa pun termasuk NaN. Ini adalah bagian dari standar IEEE 754 dan diimplementasikan oleh CPU / FPU. Ini bukan sesuatu yang JVM harus tambahkan logika untuk mendukung.

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

Perbandingan dengan NaN selalu mengembalikan hasil yang tidak terurut bahkan ketika membandingkan dengan itu sendiri. ... Predikat persamaan dan ketidaksetaraan adalah non-pensinyalan sehingga x = x return false dapat digunakan untuk menguji apakah x adalah NaN yang sunyi.

Java memperlakukan semua NaN sebagai NaN yang senyap.

Peter Lawrey
sumber
1
Apakah itu diimplementasikan oleh CPU, atau itu terprogram dalam JVM sebagai Bohemian menyebutkan?
Naweed Chougle
3
JVM harus memanggil apa pun yang akan mengimplementasikannya dengan benar. Pada PC, CPU melakukan semua pekerjaan dengan baik. Pada mesin tanpa dukungan ini, JVM harus mengimplementasikannya. (Saya tidak tahu ada mesin seperti itu)
Peter Lawrey
Kembali pada hari ketika 8087 adalah pilihan, perpustakaan C berisi emulator FP. Program seperti JVM tidak perlu khawatir tentang hal itu.
Marquis of Lorne
49

Kenapa logika itu

NaNberarti Not a Number. Apa yang bukan angka? Apa pun. Anda dapat memiliki apa pun di satu sisi dan apa pun di sisi lain, jadi tidak ada yang menjamin keduanya sama. NaNdihitung dengan Double.longBitsToDouble(0x7ff8000000000000L)dan seperti yang Anda lihat dalam dokumentasi longBitsToDouble:

Jika argumennya adalah nilai apa pun dalam rentang 0x7ff0000000000001Lmelalui 0x7fffffffffffffffLatau dalam rentang 0xfff0000000000001Lmelalui 0xffffffffffffffffL, hasilnya adalah a NaN.

Juga, NaNdiperlakukan secara logis di dalam API.


Dokumentasi

/** 
 * 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;

By the way, NaN yang diuji sebagai sampel kode Anda:

/**
 * 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);
}

Larutan

Yang bisa Anda lakukan adalah menggunakan compare/ compareTo:

Double.NaNdianggap oleh metode ini sama dengan dirinya sendiri dan lebih besar dari semua doublenilai lainnya (termasuk Double.POSITIVE_INFINITY).

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

Atau, equals:

Jika thisdan argumentkeduanya mewakili Double.NaN, maka equalsmetode kembali true, meskipun Double.NaN==Double.NaNmemiliki nilai false.

Double.NaN.equals(Double.NaN);
falsarella
sumber
Apakah Anda tahu ada kasus di mana memiliki NaN != NaNkesalahan akan membuat program lebih rumit daripada NaN != NaNbenar? Saya tahu IEEE membuat keputusan bertahun-tahun yang lalu, tetapi dari sudut pandang praktis, saya belum pernah melihat kasus yang bermanfaat. Jika suatu operasi seharusnya berjalan sampai iterasi berturut-turut menghasilkan hasil yang sama, memiliki dua iterasi berturut-turut menghasilkan NaN akan "terdeteksi secara alami" sebagai kondisi keluar jika bukan karena perilaku itu.
supercat
@supercat Bagaimana Anda bisa mengatakan bahwa dua angka acak secara alami adalah sama? Atau katakan, sama sederajat? Pikirkan NaN sebagai contoh, bukan sesuatu yang primitif. Setiap hasil abnormal yang berbeda adalah contoh berbeda dari sesuatu yang aneh dan bahkan jika keduanya harus mewakili yang sama, menggunakan == untuk contoh yang berbeda harus mengembalikan false. Di sisi lain, saat menggunakan equals dapat ditangani dengan benar seperti yang Anda inginkan. [ docs.oracle.com/javase/7/docs/api/java/lang/…
falsarella
@ falsarella: Masalahnya bukan apakah dua angka acak harus dianggap "pasti sama", melainkan dalam kasus apa yang berguna untuk membandingkan angka sebagai "pasti tidak setara" dengan dirinya sendiri. Jika seseorang mencoba menghitung batas f(f(f...f(x))), dan seseorang menemukan y=f[n](x)untuk beberapa nsehingga hasil f(y)tidak dapat dibedakan dari y, maka yakan dapat dibedakan dari hasil yang lebih mendalam f(f(f(...f(y))). Bahkan jika seseorang ingin NaN==NaNmenjadi salah, memiliki Nan!=Nan juga salah akan kurang "mengejutkan" daripada x!=xberlaku untuk beberapa x.
supercat
1
@ Falalsella: Saya percaya jenis Double.NaNtidak Double, tapi double, jadi pertanyaannya adalah yang terkait dengan perilaku double. Meskipun ada fungsi yang dapat menguji untuk hubungan kesetaraan yang melibatkan doublenilai - nilai, satu-satunya jawaban menarik yang saya tahu untuk "mengapa" (yang merupakan bagian dari pertanyaan awal) adalah "karena beberapa orang di IEEE tidak berpikir pengujian kesetaraan seharusnya mendefinisikan hubungan kesetaraan ". BTW, adakah cara idiomatis ringkas untuk menguji xdan yuntuk kesetaraan hanya menggunakan operator primitif? Semua formulasi yang saya tahu agak kikuk.
supercat
1
jawaban terbaik dan sederhana. Terima kasih
Tarun Nagpal
16

Itu mungkin bukan jawaban langsung untuk pertanyaan itu. Tetapi jika Anda ingin memeriksa apakah ada sesuatu yang sama dengan Double.NaNAnda, Anda harus menggunakan ini:

double d = Double.NaN
Double.isNaN(d);

Ini akan kembali true

JREN
sumber
6

The javadoc untuk Double.NaN mengatakan itu semua:

Konstanta yang memegang nilai Not-a-Number (NaN) tipe double. Ini setara dengan nilai yang dikembalikan oleh Double.longBitsToDouble(0x7ff8000000000000L).

Menariknya, sumber untuk Doublemendefinisikan sebagai NaNberikut:

public static final double NaN = 0.0d / 0.0;

Perilaku khusus yang Anda gambarkan adalah terprogram ke dalam JVM.

Bohemian
sumber
5
Apakah ini menggunakan kabel di JVM, atau apakah itu diimplementasikan oleh CPU seperti yang disebutkan oleh Peter?
Naweed Chougle
4

sesuai, Standar IEEE untuk aritmatika titik apung untuk angka Presisi Ganda,

Representasi standar floating point presisi ganda IEEE membutuhkan 64 bit kata, yang dapat direpresentasikan sebagai nomor dari 0 hingga 63, dari kiri ke kanan

masukkan deskripsi gambar di sini dimana,

S: Sign  1 bit
E: Exponent  11 bits
F: Fraction  52 bits 

Jika E=2047 (semua Eadalah 1) dan Fadalah nol, maka V=NaN( "Bukan angka")

Yang berarti,

Jika semua Ebit adalah 1, dan jika ada bit yang bukan nol Fmaka angkanyaNaN .

oleh karena itu, antara lain, semua nomor berikut adalah NaN ,

0 11111111 0000000000000000010000000000000000000000000000000000 = NaN
1 11111111 0000010000000000010001000000000000001000000000000000 = NaN
1 11111111 0000010000011000010001000000000000001000000000000000 = NaN

Secara khusus, Anda tidak dapat menguji

if (x == Double.NaN) 

untuk memeriksa apakah hasil tertentu sama dengan Double.NaN, karena semua nilai "bukan angka" dianggap berbeda. Namun, Anda dapat menggunakan Double.isNaNmetode ini:

if (Double.isNaN(x)) // check whether x is "not a number"
Sufiyan Ghori
sumber
3

NaN adalah nilai khusus yang menunjukkan "bukan angka"; itu adalah hasil dari operasi aritmatika tertentu yang tidak valid, seperti sqrt(-1), dan memiliki (kadang-kadang mengganggu) properti itu NaN != NaN.

Fred Foo
sumber
2

Bukan angka yang mewakili hasil operasi yang hasilnya tidak dapat diwakili dengan angka. Operasi paling terkenal adalah 0/0, yang hasilnya tidak diketahui.

Untuk alasan ini, NaN tidak sama dengan apa pun (termasuk nilai bukan angka lainnya). Untuk info lebih lanjut, cukup periksa halaman wikipedia: http://en.wikipedia.org/wiki/NaN

Matteo
sumber
-1: Ini tidak mewakili hasil 0/0. 0/0selalu NaN, tapi NaN dapat menjadi hasil dari operasi lain - seperti 2+NaN: an operation that has no mathematically definite result produces NaN, sesuai jawaban oleh @AdrianMitev
ANeves
Memang, NaN singkatan dari "Bukan Angka", dan itu adalah hasil dari semua operasi yang memiliki nilai yang tidak terdefinisi atau tidak terwakili. Operasi yang paling terkenal dan umum adalah 0/0, tetapi jelas ada banyak operasi lain yang memiliki hasil yang sama. Saya setuju bahwa jawaban saya dapat ditingkatkan, tetapi saya tidak setuju dengan -1 ... Saya baru saja memeriksa bahwa wikipedia juga menggunakan operasi 0/0 sebagai contoh operasi pertama dengan hasil NaN ( en.wikipedia.org/wiki/ NaN ).
Matteo
Juga, ini ada dalam sumber Java untuk Double: public static final double NaN = 0.0d / 0.0;
Guillaume
1
@Matteo +0, sekarang pernyataan salahnya hilang. Dan -1 atau +1 saya bukan untuk Anda setuju atau tidak setuju; tetapi baik untuk memberikan komentar dengan -1, sehingga penulis dapat memahami mengapa jawabannya dianggap tidak berguna - dan mengubahnya, jika ia menginginkannya.
ANeves
@Guillaume jika komentar itu ditujukan untuk saya, harap ulangi: saya tidak mengerti.
ANeves
0

Menurut tautan ini , ia memiliki berbagai situasi dan sulit diingat. Beginilah cara saya mengingat dan membedakan mereka. NaNberarti "secara matematis tidak terdefinisi" misalnya: "hasil 0 dibagi dengan 0 adalah tidak terdefinisi" dan karena itu tidak terdefinisi, jadi "perbandingan yang terkait dengan undefined tentu saja tidak terdefinisi". Selain itu, ini berfungsi lebih seperti premis matematika. Di sisi lain, baik tak terbatas positif dan negatif sudah ditentukan sebelumnya dan pasti, misalnya "positif atau negatif besar tak terbatas didefinisikan dengan baik secara matematis".

Tiina
sumber