Apa alasan untuk semua perbandingan yang kembali salah untuk nilai IEEE754 NaN?

267

Mengapa perbandingan nilai NaN berperilaku berbeda dari semua nilai lainnya? Artinya, semua perbandingan dengan operator ==, <=,> =, <,> di mana satu atau kedua nilai adalah NaN mengembalikan false, bertentangan dengan perilaku semua nilai lainnya.

Saya kira ini menyederhanakan perhitungan numerik dalam beberapa cara, tapi saya tidak bisa menemukan alasan yang dinyatakan secara eksplisit, bahkan dalam Catatan Kuliah tentang Status IEEE 754 oleh Kahan yang membahas keputusan desain lainnya secara rinci.

Perilaku menyimpang ini menyebabkan masalah ketika melakukan pemrosesan data sederhana. Misalnya, ketika menyortir daftar catatan wrt beberapa bidang bernilai nyata dalam program C saya perlu menulis kode tambahan untuk menangani NaN sebagai elemen maksimal, jika tidak, algoritma pengurutan dapat menjadi bingung.

Sunting: Jawaban sejauh ini semuanya berpendapat bahwa tidak ada artinya membandingkan NaNs.

Saya setuju, tetapi itu tidak berarti bahwa jawaban yang benar adalah salah, tetapi itu akan menjadi Bukan-Boolean (NaB), yang untungnya tidak ada.

Jadi pilihan mengembalikan benar atau salah untuk perbandingan dalam pandangan saya sewenang-wenang, dan untuk pemrosesan data umum akan menguntungkan jika mematuhi hukum yang biasa (refleksivitas ==, trikotomi <, ==,>), jangan sampai struktur data yang mengandalkan hukum-hukum ini menjadi bingung.

Jadi saya meminta beberapa keuntungan nyata dari melanggar undang-undang ini, bukan hanya alasan filosofis.

Sunting 2: Saya pikir saya mengerti sekarang mengapa membuat NaN maksimal adalah ide yang buruk, itu akan mengacaukan perhitungan batas atas.

NaN! = NaN mungkin diinginkan untuk menghindari mendeteksi konvergensi dalam satu loop seperti

while (x != oldX) {
    oldX = x;
    x = better_approximation(x);
}

Namun yang harus lebih baik ditulis dengan membandingkan perbedaan absolut dengan batas kecil. Jadi IMHO ini adalah argumen yang relatif lemah untuk memecahkan refleksivitas di NaN.

starblue
sumber
2
Setelah NaN memasuki perhitungan, biasanya tidak akan pernah pergi, jadi uji konvergensi Anda akan menjadi loop tanpa batas. Biasanya lebih baik melaporkan kegagalan untuk menyatu dengan rutinitas pemanggilan, mungkin dengan mengembalikan NaN. Dengan demikian, struktur loop biasanya menjadi sesuatu seperti while (fabs(x - oldX) > threshold), keluar dari loop jika konvergensi terjadi atau NaN memasuki perhitungan. Deteksi NaN dan obat yang sesuai kemudian akan terjadi di luar loop.
Stephen Canon
1
Jika NaN adalah elemen minimal dari urutan itu sementara loop masih akan berfungsi.
starblue
2
Makanan untuk dipikirkan: grouper.ieee.org/groups/1788/email/pdfmPSi1DgZZf.pdf halaman 10
starblue

Jawaban:

535

Saya adalah anggota komite IEEE-754, saya akan mencoba sedikit membantu memperjelas hal-hal.

Pertama, bilangan floating-point bukan bilangan real, dan aritmatika floating-point tidak memenuhi aksioma aritmatika nyata. Trikotomi bukan satu-satunya properti aritmatika nyata yang tidak berlaku untuk mengapung, atau bahkan yang paling penting. Sebagai contoh:

  • Selain itu tidak asosiatif.
  • Hukum distributif tidak berlaku.
  • Ada angka floating-point tanpa invers.

Saya bisa melanjutkan. Tidak mungkin untuk menentukan tipe aritmatika ukuran tetap yang memenuhi semua sifat aritmatika nyata yang kita kenal dan sukai. Komite 754 harus memutuskan untuk membengkokkan atau menghancurkan beberapa dari mereka. Ini dipandu oleh beberapa prinsip sederhana:

  1. Ketika kami bisa, kami mencocokkan perilaku aritmatika nyata.
  2. Ketika kami tidak bisa, kami mencoba membuat pelanggaran itu dapat diprediksi dan semudah mungkin untuk didiagnosis.

Mengenai komentar Anda "itu tidak berarti bahwa jawaban yang benar salah", ini salah. Predikat (y < x)bertanya apakah ykurang dari x. Jika yNaN, maka itu tidak kurang dari nilai floating-point x, jadi jawabannya tentu salah.

Saya menyebutkan bahwa trikotomi tidak berlaku untuk nilai floating-point. Namun, ada properti serupa yang memang tahan. Klausul 5.11, paragraf 2 dari standar 754-2008:

Empat hubungan yang saling eksklusif dimungkinkan: kurang dari, sama, lebih besar dari, dan tidak teratur. Kasus terakhir muncul ketika setidaknya satu operan adalah NaN. Setiap NaN harus membandingkan unordered dengan segala sesuatu, termasuk dirinya sendiri.

Sejauh menulis kode tambahan untuk menangani NaN berjalan, biasanya mungkin (walaupun tidak selalu mudah) untuk menyusun kode Anda sedemikian rupa sehingga NaN dapat lolos dengan benar, tetapi hal ini tidak selalu terjadi. Ketika tidak, beberapa kode tambahan mungkin diperlukan, tetapi itu adalah harga kecil untuk membayar kenyamanan yang diakibatkan oleh penutupan aljabar ke aritmatika titik-mengambang.


Tambahan: Banyak komentator berpendapat bahwa akan lebih berguna untuk menjaga refleksivitas kesetaraan dan trikotomi dengan alasan mengadopsi NaN! = NaN tampaknya tidak mempertahankan aksioma yang sudah dikenal. Saya mengaku memiliki simpati untuk sudut pandang ini, jadi saya pikir saya akan meninjau kembali jawaban ini dan memberikan sedikit konteks.

Pemahaman saya dari berbicara dengan Kahan adalah bahwa NaN! = NaN berasal dari dua pertimbangan pragmatis:

  • Itu x == yharus setara dengan x - y == 0bila memungkinkan (di luar menjadi teorema aritmatika nyata, ini membuat implementasi perangkat keras perbandingan lebih hemat ruang, yang paling penting pada saat standar dikembangkan - namun, perhatikan bahwa ini dilanggar untuk x = y = tak terhingga, jadi itu bukan alasan yang bagus; itu bisa dibengkokkan dengan wajar (x - y == 0) or (x and y are both NaN)).

  • Lebih penting lagi, tidak ada isnan( )predikat pada saat itu NaN diformalkan dalam aritmatika 8087; itu perlu untuk menyediakan programmer dengan cara yang mudah dan efisien untuk mendeteksi nilai-nilai NaN yang tidak bergantung pada bahasa pemrograman menyediakan sesuatu seperti isnan( )yang bisa memakan waktu bertahun-tahun. Saya akan mengutip tulisan Kahan sendiri tentang masalah ini:

Jika tidak ada cara untuk menyingkirkan NaNs, mereka akan menjadi tidak berguna seperti orang Indefinisi pada CRAYs; segera setelah seseorang ditemui, perhitungan akan lebih baik dihentikan daripada dilanjutkan untuk waktu yang tidak terbatas hingga kesimpulan yang tidak terbatas. Itulah sebabnya beberapa operasi pada NaN harus memberikan hasil non-NaN. Operasi yang mana? ... Pengecualiannya adalah predikat C “x == x” dan “x! = X”, yang masing-masing 1 dan 0 untuk setiap angka tak terbatas atau terbatas x tetapi terbalik jika x bukan Angka (NaN); ini memberikan satu-satunya perbedaan sederhana antara NaN dan angka dalam bahasa yang tidak memiliki kata untuk NaN dan predikat IsNaN (x).

Perhatikan bahwa ini juga logika yang mengesampingkan pengembalian sesuatu seperti "Tidak-Boolean". Mungkin pragmatisme ini salah tempat, dan standar seharusnya diperlukan isnan( ), tetapi itu akan membuat NaN hampir mustahil untuk digunakan secara efisien dan nyaman selama beberapa tahun sementara dunia menunggu adopsi bahasa pemrograman. Saya tidak yakin itu akan menjadi tradeoff yang masuk akal.

Terus terang: hasil NaN == NaN tidak akan berubah sekarang. Lebih baik belajar hidup dengannya daripada mengeluh di internet. Jika Anda ingin berdebat bahwa relasi pesanan yang cocok untuk wadah juga harus ada, saya akan merekomendasikan menganjurkan bahwa bahasa pemrograman favorit Anda menerapkan totalOrderpredikat standar di IEEE-754 (2008). Fakta bahwa itu belum berbicara tentang validitas perhatian Kahan yang memotivasi keadaan saat ini.

Stephen Canon
sumber
16
Saya membaca poin Anda 1 dan 2. Kemudian saya mengamati bahwa dalam aritmatika nyata (diperluas untuk memungkinkan NaN di tempat pertama) NaN sama dengan dirinya sendiri - hanya karena dalam matematika, setiap entitas sama dengan dirinya sendiri, tanpa kecuali. Sekarang saya bingung: mengapa IEEE tidak "cocok dengan perilaku aritmatika nyata", yang akan membuat NaN == NaN? Apa yang saya lewatkan?
maks
12
Sepakat; nonreflexivity NaNs telah menciptakan rasa sakit tiada akhir untuk bahasa seperti Python, dengan semantik kontainmen berbasis kesetaraan. Anda benar - benar tidak ingin kesetaraan gagal menjadi hubungan ekivalensi ketika Anda mencoba membangun wadah di atasnya. Dan memiliki dua gagasan persamaan yang terpisah juga bukan pilihan yang bersahabat, karena bahasa yang seharusnya mudah dipelajari. Hasilnya (dalam kasus Python) adalah kompromi rapuh yang tidak menyenangkan antara penghormatan terhadap IEEE 754 dan semantik kontainmen yang tidak terlalu rusak. Untungnya, jarang memasukkan NaN ke dalam wadah.
Mark Dickinson
5
Beberapa pengamatan bagus di sini: bertrandmeyer.com/2010/02/06/…
Mark Dickinson
6
@StephenCanon: Dengan cara apa (0/0) == (+ INF) + (-INF) menjadi lebih tidak masuk akal daripada dimiliki 1f/3f == 10000001f/30000002f? Jika nilai floating-point dianggap sebagai kelas ekivalensi, maka a=btidak berarti "Komputasi yang menghasilkan adan b, jika dilakukan dengan presisi tak terbatas, akan menghasilkan hasil yang identik", melainkan "Apa yang diketahui tentang acocok dengan apa yang diketahui tentang b". Saya ingin tahu apakah Anda tahu ada contoh kode di mana memiliki "Nan! = NaN" membuat hal-hal lebih sederhana daripada yang seharusnya?
supercat
5
Secara teoritis, jika Anda memiliki NaN == NaN dan tidak ada isNaN, Anda masih dapat menguji NaN dengan !(x < 0 || x == 0 || x > 0), tetapi itu akan lebih lambat dan lebih baik daripada x != x.
user2357112 mendukung Monica
50

NaN dapat dianggap sebagai keadaan / angka yang tidak ditentukan. mirip dengan konsep 0/0 yang tidak terdefinisi atau sqrt (-3) (dalam sistem bilangan real tempat floating point).

NaN digunakan sebagai semacam placeholder untuk keadaan yang tidak ditentukan ini. Secara matematis, undefined tidak sama dengan undefined. Anda juga tidak dapat mengatakan bahwa nilai yang tidak terdefinisi lebih besar atau kurang dari nilai yang tidak terdefinisi lainnya. Karenanya semua perbandingan menghasilkan false.

Perilaku ini juga menguntungkan dalam kasus di mana Anda membandingkan sqrt (-3) ke sqrt (-2). Mereka berdua akan mengembalikan NaN tetapi mereka tidak setara meskipun mereka mengembalikan nilai yang sama. Karena itu memiliki kesetaraan selalu menghasilkan false ketika berurusan dengan NaN adalah perilaku yang diinginkan.

Chris
sumber
5
Apa yang harus menjadi hasil dari sqrt (1,00000000000000022) == sqrt (1.0)? Bagaimana dengan (1E308 + 1E308-1E308-1E308-1E308) == (1E308 + 1E308)? Juga, hanya lima dari enam perbandingan yang menghasilkan false. The !=Operator mengembalikan nilai true. Memiliki NaN==NaNdan NaN!=NaNkeduanya kembali salah akan memungkinkan kode yang membandingkan x dan y untuk memilih apa yang harus terjadi ketika kedua operan NaN dengan memilih salah satu ==atau !=.
supercat
38

Untuk memasukkan analogi lain. Jika saya memberi Anda dua kotak, dan memberi tahu Anda bahwa keduanya tidak mengandung apel, apakah Anda akan memberi tahu saya bahwa kotak-kotak itu berisi hal yang sama?

NaN tidak mengandung informasi tentang apa itu sesuatu, hanya apa yang bukan. Karena itu elemen-elemen ini tidak pernah bisa dikatakan sama.

Jack Ryan
sumber
6
Semua set kosong sama, menurut definisi.
MSalters
28
Kotak yang Anda berikan TIDAK diketahui kosong.
John Smith
7
Apakah Anda memberi tahu saya bahwa kotak-kotak itu tidak berisi hal yang sama? Saya bisa mengerti alasannya (NaN==Nan)==false. Apa yang saya tidak mengerti adalah alasan untuk (Nan!=Nan)==true.
supercat
3
Saya berasumsi NaN! = NaN benar karena x! = Y didefinisikan sebagai! (X == y). Memang, saya tidak tahu apakah spesifikasi IEEE mendefinisikannya seperti itu.
Kef Schecter
6
Tetapi dalam analogi ini, jika Anda memberi saya sebuah kotak, mengatakan bahwa itu tidak mengandung apel, lalu bertanya kepada saya apakah itu setara dengan dirinya sendiri, Anda berharap saya mengatakan tidak? Karena itulah yang akan saya katakan sesuai dengan IEEE.
titik koma
12

Dari artikel wikipedia di NaN , praktik berikut dapat menyebabkan NaN:

  • Semua operasi matematika> dengan NaN sebagai setidaknya satu operan
  • Divisi 0/0, ∞ / ∞, ∞ / -∞, -∞ / ∞, dan -∞ / -∞
  • Perkalian 0 × ∞ dan 0 × -∞
  • Penambahan ∞ + (-∞), (-∞) + ∞ dan pengurangan setara.
  • Menerapkan fungsi pada argumen di luar domainnya, termasuk mengambil akar kuadrat dari angka negatif, mengambil logaritma angka negatif, mengambil garis singgung kelipatan ganjil 90 derajat (atau π / 2 radian), atau mengambil sinus terbalik atau cosinus dari angka yang kurang dari -1 atau lebih besar dari +1.

Karena tidak ada cara untuk mengetahui operasi mana yang menciptakan NaN, tidak ada cara untuk membandingkannya yang masuk akal.

Stefan Rusek
sumber
3
Selain itu, bahkan jika Anda tahu operasi mana, itu tidak akan membantu. Saya dapat membangun sejumlah rumus yang menuju ke 0/0 di beberapa titik, yang memiliki (jika kita mengasumsikan kontinuitas) nilai-nilai yang didefinisikan dengan baik dan berbeda pada titik itu.
David Thornley
4

Saya tidak tahu alasan desain, tapi ini kutipan dari standar IEEE 754-1985:

"Harus dimungkinkan untuk membandingkan angka floating-point dalam semua format yang didukung, bahkan jika format operan berbeda. Perbandingan tepat dan tidak pernah meluap atau meluap. Empat hubungan yang saling eksklusif dimungkinkan: kurang dari, sama, lebih besar dari, dan tidak berurutan. Kasus terakhir muncul ketika setidaknya satu operan adalah NaN. Setiap NaN akan membandingkan unordered dengan segala sesuatu, termasuk dirinya sendiri. "

Rick Regan
sumber
2

Ini hanya terlihat aneh karena sebagian besar lingkungan pemrograman yang memungkinkan NaNs juga tidak memungkinkan logika bernilai 3. Jika Anda memasukkan logika bernilai 3 ke dalam campuran, itu menjadi konsisten:

  • (2.7 == 2.7) = benar
  • (2.7 == 2.6) = salah
  • (2.7 == NaN) = tidak dikenal
  • (NaN == NaN) = tidak dikenal

Bahkan .NET tidak menyediakan bool? operator==(double v1, double v2)operator, sehingga Anda masih terjebak dengan (NaN == NaN) = falsehasil yang konyol .

Christian Hayter
sumber
1

Saya menduga bahwa NaN (Bukan Angka) berarti persis seperti itu: Ini bukan angka dan dengan demikian membandingkannya tidak terlalu masuk akal.

Ini sedikit seperti aritmatika dalam SQL dengan nulloperan: Semuanya menghasilkan null.

Perbandingan untuk angka floating point membandingkan nilai numerik. Dengan demikian, mereka tidak dapat digunakan untuk nilai non numerik. Oleh karena itu NaN tidak dapat dibandingkan dalam arti numerik.

Daren Thomas
sumber
3
"Ini bukan angka dan karenanya membandingkannya tidak masuk akal." String bukanlah angka tetapi membandingkannya masuk akal.
jason
2
ya, membandingkan string dengan string masuk akal. Tetapi membandingkan string dengan, katakanlah, apel, tidak masuk akal. Karena apel dan pir bukan angka, apakah masuk akal untuk membandingkannya? Mana yang lebih besar?
Daren Thomas
@ DarenThomas: Dalam SQL, tidak ada "JIKA NULL = NULL MAKA FOO;" atau "JIKA Null <> NILLAH PANGGILAN FOO;" [atau apa pun sintaksnya] akan dieksekusi FOO. Untuk menjadi setara NaN if (NaN != NaN) foo();seharusnya tidak mengeksekusi foo, tetapi itu.
supercat
1

Jawaban yang terlalu sederhana adalah bahwa NaN tidak memiliki nilai numerik, jadi tidak ada yang dapat dibandingkan dengan yang lain.

Anda dapat mempertimbangkan untuk menguji dan mengganti NaN Anda dengan + INF jika Anda ingin mereka bertindak seperti + INF.

David R Tribble
sumber
0

Sementara saya setuju bahwa perbandingan NaN dengan bilangan real mana pun harus dibatalkan, saya pikir hanya ada alasan untuk membandingkan NaN dengan dirinya sendiri. Bagaimana, misalnya apakah seseorang menemukan perbedaan antara pensinyalan NaNs dan NaNs sunyi? Jika kita menganggap sinyal sebagai satu set nilai Boolean (yaitu bit-vektor) orang mungkin bertanya apakah bit-vektor adalah sama atau berbeda dan memesan set yang sesuai. Sebagai contoh, pada decoding eksponen bias maksimum, jika signifikansi dibiarkan bergeser untuk menyelaraskan bit paling signifikan dari signifikansi pada bit paling signifikan dari format biner, nilai negatif akan menjadi NaN yang tenang dan nilai positif akan menjadi NaN pensinyalan. Nol tentu saja dicadangkan untuk tak terhingga dan perbandingannya akan dibatalkan. Penjajaran MSB akan memungkinkan untuk perbandingan sinyal langsung bahkan dari format biner yang berbeda. Oleh karena itu, dua NaN dengan set sinyal yang sama akan sama dan memberikan makna pada kesetaraan.

Patrick Campbell
sumber
-1

Bagi saya, cara termudah untuk menjelaskannya adalah:

Saya memiliki sesuatu dan jika itu bukan apel maka apakah itu jeruk?

Anda tidak dapat membandingkan NaN dengan sesuatu yang lain (bahkan sendiri) karena tidak memiliki nilai. Juga bisa berupa nilai apa saja (kecuali angka).

Saya memiliki sesuatu dan jika tidak sama dengan angka maka apakah itu sebuah string?

Halil Tevfik
sumber
Apa maksud Anda "itu bisa berupa nilai apa pun kecuali angka"?
Pushkin
-2

Karena matematika adalah bidang di mana angka "hanya ada". Dalam komputasi Anda harus menginisialisasi angka-angka itu dan mempertahankan statusnya sesuai dengan kebutuhan Anda. Di masa lalu itu, inisialisasi memori bekerja dengan cara yang tidak dapat Anda andalkan. Anda tidak pernah bisa membiarkan diri Anda berpikir tentang ini "oh, itu akan diinisialisasi dengan 0xCD sepanjang waktu, algo saya tidak akan rusak" .

Jadi Anda perlu pelarut non-pencampuran yang tepat yang cukup lengket untuk tidak membiarkan algoritma Anda tersedot dan rusak. Algoritma yang baik yang melibatkan angka sebagian besar akan bekerja dengan relasi, dan mereka yang jika () relasi akan dihilangkan.

Ini hanyalah pelumas yang dapat Anda masukkan ke variabel baru saat pembuatan, alih-alih pemrograman acak dari memori komputer. Dan algoritma Anda apa pun itu, tidak akan rusak.

Selanjutnya, ketika Anda masih tiba-tiba menemukan bahwa algoritma Anda menghasilkan NaN, dimungkinkan untuk membersihkannya, melihat ke setiap cabang satu per satu. Sekali lagi, aturan "selalu salah" sangat membantu dalam hal ini.

sanaris
sumber
-4

Jawaban yang sangat singkat:

Karena berikut ini: nan / nan = 1 TIDAK boleh ditahan. Kalau tidak, inf/infakan menjadi 1.

(Oleh karena itu nantidak bisa sama dengan nan. Adapun >atau <, jika nanakan menghormati hubungan pesanan dalam set memuaskan properti Archimedean, kita akan memiliki lagi nan / nan = 1pada batas).

SeF
sumber
2
Tidak, itu tidak masuk akal. Kami sudah inf = infdan inf / inf = nan, jadi nan = nantidak akan mencegahnya nan / nan = nan.
starblue
@ starblue Maksud Anda nan / nan = 1? Pokoknya ... Alasanmu masuk akal jika inf dan nan sama seperti nomor lainnya. Bukan itu masalahnya. Alasan mengapa inf/infharus nan(atau bentuk tak tentu dalam matematika) dan tidak 1lebih halus daripada manipulasi aljabar sederhana (lihat teorema De L'Hospital).
SeF