Sekadar catatan, 1 ons pencegahan lebih baik dari 1 lb penyembuhan. Dengan kata lain, mencegah 0.f / 0.f agar tidak pernah dieksekusi jauh lebih baik daripada memeriksa surutnya nankode Anda. nanHal ini dapat sangat merusak program Anda, jika dibiarkan berkembang biak, itu dapat menyebabkan sulit untuk menemukan bug. Ini karena nanberacun, (5 * nan= nan), nantidak sama dengan apa pun ( nan! = nan), nanTidak lebih besar dari apa pun ( nan!> 0), nantidak kurang dari apa pun ( nan! <0).
bobobobo
1
@obobobo: Itu fitur, memungkinkan pemeriksaan kesalahan terpusat. Sama seperti pengecualian vs nilai pengembalian.
Ben Voigt
2
Mengapa <cmath> tidak memiliki isnan ()? Ada di std ::
frankliuao
Jawaban:
351
Menurut standar IEEE, nilai NaN memiliki properti ganjil yang perbandingannya selalu salah. Artinya, untuk float f, f != fakan benar hanya jika f adalah NaN.
Perhatikan bahwa, seperti yang ditunjukkan beberapa komentar di bawah, tidak semua kompiler menghargai ini ketika mengoptimalkan kode.
Untuk kompiler mana pun yang mengklaim menggunakan IEEE floating point, trik ini harus bekerja. Tetapi saya tidak dapat menjamin bahwa itu akan berhasil dalam praktek. Periksa dengan kompiler Anda, jika ragu.
Kompiler sebaiknya tidak menghapus ini jika berjalan dalam mode IEEE. Periksa dokumentasi untuk compiler Anda, tentu saja ...
dmckee --- mantan moderator kitten
38
-1 hanya bekerja secara teori, bukan dalam praktiknya: kompiler seperti g ++ (with -fastmath) mengacaukannya. satu-satunya cara umum, sampai c ++ 0x, adalah menguji bitpattern.
Ceria dan hth. - Alf
66
@Alf: Dokumentasi untuk -ffast-mathopsi secara eksplisit mengatakan bahwa ia dapat menghasilkan output yang salah untuk program yang bergantung pada implementasi yang tepat jika IEEE atau ISO aturan / spesifikasi untuk fungsi matematika. Tanpa opsi itu diaktifkan, menggunakan x != xadalah cara pengujian yang valid dan portabel untuk NaN.
Adam Rosenfield
7
@ Adam: dokumentasi memang secara terbuka menyatakan bahwa itu tidak sesuai, ya. dan ya saya telah menemukan argumen itu sebelumnya, mendiskusikan ini panjang lebar dengan Gabriel Dos Reis. itu biasanya digunakan untuk mempertahankan desain, dalam argumen melingkar (saya tidak tahu apakah Anda bermaksud mengaitkannya dengan itu, tetapi perlu diketahui - itu adalah perang api). Kesimpulan Anda yang x != xvalid tanpa opsi itu tidak mengikuti secara logis. mungkin benar untuk versi g ++ tertentu, atau tidak. Lagi pula, Anda umumnya tidak memiliki cara untuk menjamin bahwa opsi fastmath tidak akan digunakan.
Ceria dan hth. - Alf
7
@Alf: Tidak, saya tidak mengetahui diskusi Anda dengan Gabriel Dos Reis. Steve Jessop membuat poin bagus dalam pertanyaan lain tentang asumsi representasi IEEE. Jika Anda mengasumsikan IEEE 754 dan bahwa kompiler beroperasi dengan cara yang sesuai (yaitu tanpa -ffast-mathopsi), maka itu x != xadalah solusi yang valid dan portabel. Anda bahkan dapat menguji -ffast-mathdengan menguji __FAST_MATH__makro dan beralih ke implementasi yang berbeda dalam kasus itu (misalnya menggunakan serikat pekerja dan sedikit memutar-mutar).
Adam Rosenfield
220
Tidak ada isnan()fungsi yang tersedia di C ++ Standard Library saat ini. Itu diperkenalkan di C99 dan didefinisikan sebagai makro bukan fungsi. Elemen-elemen pustaka standar yang didefinisikan oleh C99 bukan bagian dari standar C ++ saat ini ISO / IEC 14882: 1998 juga tidak pembaruan ISO / IEC 14882: 2003
Pada tahun 2005 Laporan Teknis 1 diusulkan. TR1 menghadirkan kompatibilitas dengan C99 ke C ++. Terlepas dari kenyataan itu tidak pernah secara resmi diadopsi menjadi standar C ++, banyak ( GCC 4.0+ atau Visual C ++ 9.0+ implementasi C ++ memang menyediakan fitur TR1, semuanya atau hanya beberapa (Visual C ++ 9.0 tidak menyediakan fungsi matematika C99) .
Jika TR1 tersedia, maka cmathsertakan elemen C99 seperti isnan(),, isfinite()dll. Tetapi mereka didefinisikan sebagai fungsi, bukan makro, biasanya di std::tr1::namespace, meskipun banyak implementasi (yaitu GCC 4+ di Linux atau di XCode pada Mac OS X 10.5+) menyuntikkannya langsung ke std::, jadi std::isnandidefinisikan dengan baik.
Selain itu, beberapa implementasi C ++ masih membuat isnan()makro C99 tersedia untuk C ++ (termasuk melalui cmathatau math.h), yang dapat menyebabkan lebih banyak kebingungan dan pengembang mungkin menganggapnya sebagai perilaku standar.
Catatan tentang Viusal C ++, seperti yang disebutkan di atas, tidak menyediakan std::isnankeduanya std::tr1::isnan, tetapi menyediakan fungsi ekstensi _isnan()yang telah tersedia sejak Visual C ++ 6.0
Di XCode, ada yang lebih menyenangkan. Seperti yang disebutkan, GCC 4+ mendefinisikan std::isnan. Untuk versi yang lebih lama dari kompiler dan bentuk perpustakaan XCode, tampaknya (di sini adalah diskusi yang relevan ), belum memiliki kesempatan untuk memeriksa sendiri) dua fungsi didefinisikan, __inline_isnand()pada Intel dan __isnand()pada Power PC.
Semua orang menginginkan fungsi-fungsi ini seperti isNan atau isInfinity. Mengapa orang yang bertanggung jawab tidak hanya memasukkan standar mereka ???? - Saya akan mencoba mencari tahu cara bertanggung jawab dan memberikan suara saya untuk ini. Serius.
shuhalo
8
@shuhalo Sudah bertanggung jawab?
Tomáš Zato - Reinstate Monica
11
Jawaban ini harus diperbarui karena std::isnansekarang merupakan bagian dari standar C ++ 11 dan dukungan telah menyebar. std :: isnan diimplementasikan di Visual Studio dimulai dengan Visual Studio 2013. Mungkin @shuhalo bertanggung jawab :-)
aberaud
170
Solusi pertama: jika Anda menggunakan C ++ 11
Karena ini ditanyakan ada sedikit perkembangan baru: penting untuk mengetahui bahwa itu std::isnan()adalah bagian dari C ++ 11
Harap perhatikan bahwa ini tidak kompatibel dengan -fast-math jika Anda menggunakan g ++, lihat saran di bawah ini.
Solusi lain: jika Anda menggunakan alat yang tidak sesuai C ++ 11
Untuk C99, di C, ini diimplementasikan sebagai makro isnan(c)yang mengembalikan nilai int. Jenis xharus mengambang, ganda atau panjang ganda.
Berbagai vendor mungkin atau mungkin tidak termasuk atau tidak fungsi isnan().
Cara yang seharusnya portabel untuk memeriksa NaNadalah dengan menggunakan properti IEEE 754 yang NaNtidak sama dengan dirinya sendiri: yaitu x == xakan salah untuk xmenjadi NaN.
Namun opsi terakhir mungkin tidak berfungsi dengan setiap kompiler dan beberapa pengaturan (terutama pengaturan optimisasi), jadi di pilihan terakhir, Anda selalu dapat memeriksa pola bit ...
Jelas layak menjadi jawaban yang diterima dan layak mendapat lebih banyak upvotes. Terima kasih atas tipnya
LBes
3
−1std::isnan masih merupakan rekomendasi ungood pada Februari 2017, karena tidak bekerja dengan optimalisasi floating point g ++.
Ceria dan hth. - Alf
@ Cheersandhth.-Alf: apakah opsi ini sesuai dengan IEEE? Jawabannya telah diedit
BlueTrin
@BlueTrin: Keduanya x != xdan isnandiharuskan bekerja untuk kepatuhan IEEE 754. Mengenai yang terakhir, standar IEEE 754-2008 menyatakan bahwa "Implementasi harus menyediakan operasi non-komputasi berikut untuk semua format aritmatika yang didukung" dan "isNaN (x) benar jika dan hanya jika x adalah NaN". Untuk memeriksa kesesuaian yang dibutuhkan oleh standar is754version1985()dan is754version2008(), di mana C ++ menawarkan std::numeric_limits<Fp>::is_iec559()(IEC 559 adalah standar yang sama). Sayangnya dengan -ffast-mathoptimisasi, mis. G ++ mengklaim kesesuaian tetapi tidak sesuai.
Ceria dan hth. - Alf
1
Peringatan: isnan (x) tidak bekerja dengan opsi -ffinite-math-only dalam gcc dan clang
A Fog
82
Ada juga pustaka header-only yang ada di Boost yang memiliki alat yang rapi untuk menangani tipe data floating point
telah ditambahkan di Boost 1.35 (saya baru saja menemukan program saya tidak dapat dikompilasi di distro linux lama).
marcin
2
jika Anda mengompilasi dengan opsi --fast-math maka fungsi ini tidak akan berfungsi seperti yang diharapkan.
Gaetano Mendola
43
Ada tiga cara "resmi": posix isnanmakro , isnantemplat fungsi c ++ 0x , atau _isnanfungsi visual c ++ .
Sayangnya agak tidak praktis untuk mendeteksi mana yang akan digunakan.
Dan sayangnya, tidak ada cara yang dapat diandalkan untuk mendeteksi apakah Anda memiliki representasi IEEE 754 dengan NaNs. Perpustakaan standar menawarkan cara resmi ( numeric_limits<double>::is_iec559). Tetapi dalam praktiknya kompiler seperti g ++ sekrup itu.
Secara teori orang dapat menggunakan secara sederhana x != x, tetapi kompiler seperti g ++ dan visual c ++ mengacaukannya.
Jadi pada akhirnya, uji untuk bitpatterns NaN tertentu , dengan asumsi (dan semoga menegakkan, di beberapa titik!) Representasi tertentu seperti IEEE 754.
EDIT : sebagai contoh "kompiler seperti g ++ ... mengacaukannya", pertimbangkan
#include<limits>#include<assert.h>void foo(double a,double b ){
assert( a != b );}int main(){typedef std::numeric_limits<double>Info;doubleconst nan1 =Info::quiet_NaN();doubleconst nan2 =Info::quiet_NaN();
foo( nan1, nan2 );}
Kompilasi dengan g ++ (TDM-2 mingw32) 4.4.1:
C: \ test> ketik "C: \ Program Files \ @commands \ gnuc.bat"
@rem -finput-charset = windows-1252
@ g ++ -O -pedantic -std = c ++ 98 -Wall -Wwrite-string% * -Wno-long-long
C: \ test> gnuc x.cpp
C: \ test> a && echo berfungsi ... || gaung! gagal
bekerja ...
C: \ test> gnuc x.cpp --fast-math
C: \ test> a && echo berfungsi ... || gaung! gagal
Pernyataan gagal: a! = B, file x.cpp, baris 6
Aplikasi ini telah meminta Runtime untuk menghentikannya dengan cara yang tidak biasa.
Silakan hubungi tim dukungan aplikasi untuk informasi lebih lanjut.
!gagal
C: \ test> _
@Alf: Contoh Anda berfungsi seperti yang diharapkan untuk saya pada Mac OS X dan Linux pada berbagai versi g ++ antara 4.0 dan 4.5. Dokumentasi untuk -ffast-mathopsi secara eksplisit mengatakan bahwa ia dapat menghasilkan output yang salah untuk program yang bergantung pada implementasi yang tepat jika IEEE atau ISO aturan / spesifikasi untuk fungsi matematika. Tanpa opsi itu diaktifkan, menggunakan x != xadalah cara pengujian yang valid dan portabel untuk NaN.
Adam Rosenfield
6
@ Adam: Apa yang Anda lewatkan adalah bahwa standar C ++ tidak memerlukan representasi IEEE atau matematika untuk float. Sejauh halaman manual memberitahu Anda, gcc -ffast-mathmasih implementasi C ++ yang sesuai (baik, dengan asumsi itu numeric_limits::is_iec559benar, itu, meskipun Alf menyarankan di atas bahwa tidak): C ++ kode mengandalkan IEEE bukan C ++ portabel dan tidak memiliki hak untuk mengharapkan implementasi untuk menyediakannya.
Steve Jessop
5
Dan Alf benar, tes cepat pada gcc 4.3.4 dan is_iec559benar dengan -ffast-math. Jadi masalahnya di sini adalah bahwa dokumen GCC -ffast-mathhanya mengatakan bahwa itu bukan IEEE / ISO untuk fungsi matematika, sedangkan mereka harus mengatakan bahwa itu bukan C ++, karena implementasinya numeric_limitsborked. Saya kira GCC tidak bisa selalu tahu kapan template didefinisikan, apakah backend akhirnya benar-benar sesuai dengan float, dan bahkan tidak mencoba. IIRC ada masalah serupa dalam daftar bug yang beredar untuk kesesuaian C99 GCC.
Steve Jessop
1
@Alf, @Steve, saya tidak tahu standar C ++ tidak memiliki spesifikasi tentang nilai floating-point. Cukup mengejutkan bagi saya. Ini terlihat lebih baik menangani IEEE 754 dan NaN sebagai ekstensi platform spesifik daripada standar. Bukan? Dan bisakah saya mengharapkan segala jenis isnan () atau IEEE754 ditambahkan dalam C ++ 0x?
Eonil
3
@Eonil: C ++ 0x masih memiliki misalnya "Representasi nilai dari tipe titik-mengambang adalah implementasi-didefinisikan". C dan C ++ keduanya bertujuan untuk mendukung implementasi pada mesin tanpa perangkat keras floating-point, dan pelampung IEEE 754 yang tepat bisa sedikit lebih lambat untuk ditiru daripada alternatif yang cukup akurat. Teorinya adalah Anda dapat menegaskan is_iec559jika Anda membutuhkan IEEE, dalam praktiknya tampaknya tidak berfungsi pada GCC. C ++ 0x memang memiliki isnanfungsi, tetapi karena GCC tidak mengimplementasikan dengan benar is_iec559sekarang, saya kira itu tidak akan di C ++ 0x juga, dan -ffast-mathmungkin akan merusaknya isnan.
Steve Jessop
39
Ada std :: isnan jika Anda kompiler mendukung ekstensi c99, tapi saya tidak yakin apakah mingw melakukannya.
Ini adalah fungsi kecil yang seharusnya berfungsi jika kompiler Anda tidak memiliki fungsi standar:
bool custom_isnan(double var){volatiledouble d = var;return d != d;}
Ketika melakukan itu adalah kesempatan mereka, kompiler akan mengoptimalkan perbandingan keluar, selalu kembali benar.
CTT
23
Tidak ada. Kompiler yang melakukan itu rusak. Anda mungkin juga mengatakan bahwa ada kemungkinan perpustakaan standar isnanmengembalikan hasil yang salah. Secara teknis benar, kompiler bisa buggy, tetapi dalam praktiknya, Not Gonna Happen. Sama seperti var != var. Ia bekerja karena itulah cara nilai-nilai floating point IEEE didefinisikan.
Jalf
29
jika -fast-matematika disetel, isnan () akan gagal mengembalikan hasil yang benar untuk gcc. Tentu saja, optimasi ini didokumentasikan sebagai melanggar semantik IEEE ...
Matthew Herrmann
Jika -Fast-Matematika diatur, maka kompiler buggy. Atau lebih tepatnya, jika -Fast-Matematika diatur, semua taruhan dimatikan dan Anda tidak dapat mengandalkan NaNs pula.
Adrian Ratnapala
25
Anda dapat menggunakan numeric_limits<float>::quiet_NaN( )definisi di limitsperpustakaan standar untuk mengujinya. Ada konstanta terpisah yang ditentukan untuk double.
#include<iostream>#include<math.h>#include<limits>usingnamespace std;int main(){
cout <<"The quiet NaN for type float is: "<< numeric_limits<float>::quiet_NaN()<< endl;float f_nan = numeric_limits<float>::quiet_NaN();if( isnan(f_nan)){
cout <<"Float was Not a Number: "<< f_nan << endl;}return0;}
Saya tidak tahu apakah ini berfungsi pada semua platform, karena saya hanya diuji dengan g ++ di Linux.
Hati-hati, meskipun - tampaknya ada bug di numeric_limits dalam GCC versi 3.2.3, karena ia mengembalikan 0,0 untuk quiet_NaN. Versi GCC nanti tidak masalah dalam pengalaman saya.
Nathan Kitchen
@Nathan: Senang tahu. Saya menggunakan versi 4.3.2, jadi saya baik-baik saja.
Bill the Lizard
18
Anda dapat menggunakan isnan()fungsi ini, tetapi Anda harus menyertakan perpustakaan C matematika.
#include<cmath>
Karena fungsi ini adalah bagian dari C99, maka tidak tersedia di mana-mana. Jika vendor Anda tidak menyediakan fungsi, Anda juga dapat menentukan varian Anda sendiri untuk kompatibilitas.
Saya menggunakan <cmath> dan tidak ada isnan di dalamnya! kebetulan saya menemukan bahwa ada adalah sebuah isnandi <math.h>
Hasen
1
Seperti yang saya katakan, ini adalah bagian dari C99. Karena C99 bukan bagian dari standar C ++ saat ini, saya memberikan alternatifnya. Tetapi karena kemungkinan isnan () akan dimasukkan dalam standar C ++ yang akan datang, saya menempatkan arahan #ifndef di sekitarnya.
raimue
12
Kode berikut menggunakan definisi NAN (semua bit eksponen yang ditetapkan, setidaknya satu bit bit yang ditetapkan) dan mengasumsikan bahwa sizeof (int) = sizeof (float) = 4. Anda dapat mencari NAN di Wikipedia untuk detailnya.
Saya percaya ini juga akan bekerja pada platform big endian. Secara literal 0x7fffffffhanya akan duduk di memori sebagai ff ff ff 7f. valuememiliki urutan yang sama seperti halnya 0x7f800000, sehingga semua operasi berbaris (tidak ada pertukaran byte). Saya akan tertarik jika seseorang dapat menguji ini pada platform big endian.
Bryan W. Wagner
0x7fff1234juga merupakan NaN. Begitu juga0xffffffff
Steve Hollasch
12
pencegahan nan
Jawaban saya untuk pertanyaan ini adalah jangan gunakan cek surut untuknan . Sebaliknya, gunakan pemeriksaan preventif untuk pembagian formulir 0.0/0.0.
#include<float.h>float x=0.f;// I'm gonna divide by x!if(!x )// Wait! Let me check if x is 0
x = FLT_MIN ;// oh, since x was 0, i'll just make it really small instead.float y =0.f/ x ;// whew, `nan` didn't appear.
nanhasil dari operasi 0.f/0.f, atau 0.0/0.0. nanadalah musuh buruk bagi stabilitas kode Anda yang harus dideteksi dan dicegah dengan sangat hati-hati 1 . Properti nanyang berbeda dari angka normal:
nanberacun, (5 * nan= nan)
nantidak sama dengan apa pun, bahkan tidak sendiri ( nan! = nan)
nantidak lebih besar dari apa pun ( nan!> 0)
nantidak kurang dari apa pun ( nan! <0)
2 properti terakhir yang terdaftar adalah kontra-logis dan akan menghasilkan perilaku aneh kode yang bergantung pada perbandingan dengan nanangka (properti ke-3 terakhir juga aneh, tetapi Anda mungkin tidak akan pernah melihat x != x ?dalam kode Anda (kecuali jika Anda memeriksa untuk nan (unreliably))).
Dalam kode saya sendiri, saya perhatikan bahwa nannilai cenderung menghasilkan bug yang sulit ditemukan. (Perhatikan bagaimana ini bukan kasus untuk infatau -inf. ( -inf<0) mengembalikan TRUE, (0 < inf) mengembalikan BENAR, dan bahkan ( -inf< inf) mengembalikan BENAR. Jadi, dalam pengalaman saya, perilaku kode sering masih seperti yang diinginkan).
apa yang harus dilakukan di bawah nan
Apa yang Anda inginkan terjadi di bawah 0.0/0.0harus ditangani sebagai kasus khusus , tetapi apa yang Anda lakukan harus bergantung pada angka yang Anda harapkan keluar dari kode.
Pada contoh di atas, pada dasarnya hasil ( 0.f/FLT_MIN) adalah 0. Anda mungkin ingin 0.0/0.0menghasilkan HUGEgantinya. Begitu,
float x=0.f, y=0.f, z;if(!x &&!y )// 0.f/0.f case
z = FLT_MAX ;// biggest float possibleelse
z = y/x ;// regular division.
Jadi di atas, jika x adalah 0.f, infakan menghasilkan (yang memiliki perilaku cukup baik / tidak rusak seperti yang disebutkan di atas sebenarnya).
Ingat, pembagian integer dengan 0 menyebabkan pengecualian runtime . Jadi Anda harus selalu memeriksa pembagian integer dengan 0. Hanya karena dengan 0.0/0.0tenang mengevaluasi nantidak berarti Anda bisa malas dan tidak memeriksa 0.0/0.0sebelum itu terjadi.
1 Pemeriksaan nanvia x != xterkadang tidak dapat diandalkan ( x != xdilucuti oleh beberapa kompiler pengoptimal yang merusak kepatuhan IEEE, khususnya ketika -ffast-mathsakelar diaktifkan).
Terima kasih telah menunjukkan ini; pemrograman seperti itu pasti akan membantu masalah seperti itu. Tapi lain kali, tolong jangan terlalu banyak menyalahgunakan fitur format teks. Mengubah ukuran font, berat dan gaya seperti itu membuatnya sangat sulit untuk dibaca.
Magnus
4
Perhatikan bahwa 0,0 / 0,0 bukan satu-satunya operasi yang mungkin menghasilkan NaN. Akar kuadrat dari angka negatif mengembalikan NaN. Cosinus + infinity mengembalikan NaN juga. operasi acos (x) di mana x tidak dalam kisaran [0, pi] juga dapat menghasilkan NaN. Singkatnya, kita harus ekstra hati-hati untuk juga melihat operasi yang berpotensi berisiko ini, tidak hanya untuk 0,0 / 0,0.
Boris Dalstein
Sepenuhnya setuju dengan Boris. Dalam pengalaman saya, NaN praktis selalu datang dari sesuatu seperti sqrt (-1.302e-53), yaitu hasil perhitungan menengah yang mendekati nol dimasukkan ke dalam sqrt tanpa memeriksa negativitas.
hans_meine
1
"Mencegah NaNs" berarti Anda harus masuk ke dalam semua operasi aritmatika dasar, bukan hanya pembagian. Anda harus berhati-hati terhadap ∞ / ∞, 0 * ∞, ∞% x, x% 0, ∞ - ∞, 0 ^ 0, ∞ ^ 0, di antara banyak lainnya. Menjadi "preventif" dengan operasi aritmatika dasar seperti itu berarti Anda akan benar-benar menahan kinerja Anda (dan kemungkinan kehilangan kasus tambahan yang tidak Anda pikirkan).
Steve Hollasch
11
Pada C ++ 14 ada beberapa cara untuk menguji apakah angka floating point valueadalah NaN.
Dari cara-cara ini, hanya memeriksa bit representasi angka, bekerja dengan andal, seperti yang disebutkan dalam jawaban asli saya. Khususnya, std::isnandan pemeriksaan yang sering diajukan v != v, tidak bekerja dengan andal dan tidak boleh digunakan, jangan sampai kode Anda berhenti bekerja dengan benar ketika seseorang memutuskan bahwa optimasi floating point diperlukan, dan meminta kompiler untuk melakukan itu. Situasi ini dapat berubah, kompiler bisa mendapatkan lebih banyak menyesuaikan diri, tetapi untuk masalah ini yang belum terjadi dalam 6 tahun sejak jawaban asli.
Selama sekitar 6 tahun, jawaban awal saya adalah solusi yang dipilih untuk pertanyaan ini, yang OK. Tetapi baru-baru ini jawaban yang sangat tervotasikan merekomendasikan v != vtes tidak dapat diandalkan telah dipilih. Oleh karena itu, ini jawaban tambahan yang lebih baru (kami sekarang memiliki standar C ++ 11 dan C ++ 14, dan C ++ 17 di cakrawala).
Cara utama untuk memeriksa NaN-ness, pada C ++ 14, adalah:
std::isnan(value) )
adalah cara pustaka standar yang dimaksudkan sejak C ++ 11. isnantampaknya bertentangan dengan makro Posix dengan nama yang sama, tetapi dalam praktiknya itu tidak masalah. Masalah utama adalah bahwa ketika optimasi aritmatika floating point diminta, maka dengan setidaknya satu kompiler utama, yaitu g ++, std::isnankembali falseuntuk argumen NaN .
(fpclassify(value) == FP_NAN) )
Menderita masalah yang sama seperti std::isnan, yaitu, tidak dapat diandalkan.
(value != value) )
Direkomendasikan dalam banyak jawaban SO. Menderita masalah yang sama seperti std::isnan, yaitu, tidak dapat diandalkan.
(value == Fp_info::quiet_NaN()) )
Ini adalah tes yang dengan perilaku standar tidak boleh mendeteksi NaN, tetapi bahwa dengan perilaku yang dioptimalkan mungkin bisa mendeteksi NaN (karena kode yang dioptimalkan hanya membandingkan representasi bitlevel secara langsung), dan mungkin dikombinasikan dengan cara lain untuk mencakup perilaku standar yang tidak dioptimalkan. , andal bisa mendeteksi NaN. Sayangnya ternyata tidak berfungsi dengan baik.
(ilogb(value) == FP_ILOGBNAN) )
Menderita masalah yang sama seperti std::isnan, yaitu, tidak dapat diandalkan.
isunordered(1.2345, value) )
Menderita masalah yang sama seperti std::isnan, yaitu, tidak dapat diandalkan.
is_ieee754_nan( value ) )
Ini bukan fungsi standar. Ini memeriksa bit sesuai dengan standar IEEE 754. Ini benar-benar dapat diandalkan tetapi kode ini agak bergantung pada sistem.
Dalam kode uji lengkap berikut "sukses" adalah apakah suatu ekspresi melaporkan Nannnes dari nilai tersebut. Untuk sebagian besar ekspresi ukuran keberhasilan ini, tujuan mendeteksi NaN dan hanya NaN, sesuai dengan semantik standar mereka. Untuk (value == Fp_info::quiet_NaN()) )ekspresi, namun, perilaku standar adalah bahwa hal itu tidak bekerja sebagai NaN-detektor.
Hasil dengan g ++ (perhatikan lagi bahwa perilaku standar (value == Fp_info::quiet_NaN())adalah bahwa itu tidak berfungsi sebagai detektor NaN, itu hanya sangat menarik di sini):
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> g ++ --version | temukan "++"
g ++ (x86_64-win32-sjlj-rev1, Dibangun oleh proyek MinGW-W64) 6.3.0
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> g ++ foo.cpp && a
Klaim kompiler IEEE 754 = true
v = nan, (std :: isnan (value)) = Kesuksesan sejati
u = 3.14, (std :: isnan (value)) = Keberhasilan palsu
w = inf, (std :: isnan (value)) = Keberhasilan palsu
v = nan, ((fpclassify (value) == 0x0100)) = Sukses sejati
u = 3.14, ((fpclassify (value) == 0x0100)) = Keberhasilan palsu
w = inf, ((fpclassify (value) == 0x0100)) = Keberhasilan palsu
v = nan, ((nilai! = nilai)) = Kesuksesan sejati
u = 3,14, ((nilai! = nilai)) = Keberhasilan palsu
w = inf, ((value! = value)) = false Success
v = nan, ((nilai == Fp_info :: quiet_NaN ())) = false GAGAL
u = 3,14, ((nilai == Fp_info :: quiet_NaN ())) = Keberhasilan palsu
w = inf, ((value == Fp_info :: quiet_NaN ())) = Keberhasilan palsu
v = nan, ((ilogb (nilai) == ((int) 0x80000000))) = Sukses sejati
u = 3,14, ((ilogb (nilai) == ((int) 0x80000000))) = Keberhasilan palsu
w = inf, ((ilogb (value) == ((int) 0x80000000)))) Sukses salah
v = nan, (isunordered (1.2345, value)) = true Success
u = 3,14, (isunordered (1.2345, value)) = false Success
w = inf, (isunordered (1.2345, value)) = false Success
v = nan, (is_ieee754_nan (value)) = Kesuksesan sejati
u = 3,14, (is_ieee754_nan (nilai)) = Keberhasilan palsu
w = inf, (is_ieee754_nan (value)) = Keberhasilan palsu
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> g ++ foo.cpp -fast-math && a
Klaim kompiler IEEE 754 = true
v = nan, (std :: isnan (value)) = false GAGAL
u = 3.14, (std :: isnan (value)) = Keberhasilan palsu
w = inf, (std :: isnan (value)) = Keberhasilan palsu
v = nan, ((fpclassify (value) == 0x0100)) = false GAGAL
u = 3.14, ((fpclassify (value) == 0x0100)) = Keberhasilan palsu
w = inf, ((fpclassify (value) == 0x0100)) = Keberhasilan palsu
v = nan, ((nilai! = nilai)) = false GAGAL
u = 3,14, ((nilai! = nilai)) = Keberhasilan palsu
w = inf, ((value! = value)) = false Success
v = nan, ((value == Fp_info :: quiet_NaN ())) = Sukses sejati
u = 3,14, ((nilai == Fp_info :: quiet_NaN ())) = true FAILED
w = inf, ((value == Fp_info :: quiet_NaN ())) = true FAILED
v = nan, ((ilogb (nilai) == ((int) 0x80000000))) = Sukses sejati
u = 3,14, ((ilogb (nilai) == ((int) 0x80000000))) = Keberhasilan palsu
w = inf, ((ilogb (value) == ((int) 0x80000000)))) Sukses salah
v = nan, (isunordered (1.2345, value)) = false GAGAL
u = 3,14, (isunordered (1.2345, value)) = false Success
w = inf, (isunordered (1.2345, value)) = false Success
v = nan, (is_ieee754_nan (value)) = Kesuksesan sejati
u = 3,14, (is_ieee754_nan (nilai)) = Keberhasilan palsu
w = inf, (is_ieee754_nan (value)) = Keberhasilan palsu
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> _
Hasil dengan Visual C ++:
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> cl / nologo- 2> & 1 | temukan "++"
Microsoft (R) C / C ++ Mengoptimalkan Versi Kompiler 19.00.23725 untuk x86
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> cl foo.cpp / Feb && b
foo.cpp
Klaim kompiler IEEE 754 = true
v = nan, (std :: isnan (value)) = Kesuksesan sejati
u = 3.14, (std :: isnan (value)) = Keberhasilan palsu
w = inf, (std :: isnan (value)) = Keberhasilan palsu
v = nan, ((fpclassify (value) == 2)) = Kesuksesan sejati
u = 3,14, ((fpclassify (value) == 2)) = Keberhasilan palsu
w = inf, ((fpclassify (value) == 2)) = Keberhasilan palsu
v = nan, ((nilai! = nilai)) = Kesuksesan sejati
u = 3,14, ((nilai! = nilai)) = Keberhasilan palsu
w = inf, ((value! = value)) = false Success
v = nan, ((nilai == Fp_info :: quiet_NaN ())) = false GAGAL
u = 3,14, ((nilai == Fp_info :: quiet_NaN ())) = Keberhasilan palsu
w = inf, ((value == Fp_info :: quiet_NaN ())) = Keberhasilan palsu
v = nan, ((ilogb (value) == 0x7fffffff)) = Sukses sejati
u = 3,14, ((ilogb (nilai) == 0x7fffffff)) = Keberhasilan palsu
w = inf, ((ilogb (value) == 0x7fffffff)) = true GAGAL
v = nan, (isunordered (1.2345, value)) = true Success
u = 3,14, (isunordered (1.2345, value)) = false Success
w = inf, (isunordered (1.2345, value)) = false Success
v = nan, (is_ieee754_nan (value)) = Kesuksesan sejati
u = 3,14, (is_ieee754_nan (nilai)) = Keberhasilan palsu
w = inf, (is_ieee754_nan (value)) = Keberhasilan palsu
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> cl foo.cpp / Feb / fp: cepat && b
foo.cpp
Klaim kompiler IEEE 754 = true
v = nan, (std :: isnan (value)) = Kesuksesan sejati
u = 3.14, (std :: isnan (value)) = Keberhasilan palsu
w = inf, (std :: isnan (value)) = Keberhasilan palsu
v = nan, ((fpclassify (value) == 2)) = Kesuksesan sejati
u = 3,14, ((fpclassify (value) == 2)) = Keberhasilan palsu
w = inf, ((fpclassify (value) == 2)) = Keberhasilan palsu
v = nan, ((nilai! = nilai)) = Kesuksesan sejati
u = 3,14, ((nilai! = nilai)) = Keberhasilan palsu
w = inf, ((value! = value)) = false Success
v = nan, ((nilai == Fp_info :: quiet_NaN ())) = false GAGAL
u = 3,14, ((nilai == Fp_info :: quiet_NaN ())) = Keberhasilan palsu
w = inf, ((value == Fp_info :: quiet_NaN ())) = Keberhasilan palsu
v = nan, ((ilogb (value) == 0x7fffffff)) = Sukses sejati
u = 3,14, ((ilogb (nilai) == 0x7fffffff)) = Keberhasilan palsu
w = inf, ((ilogb (value) == 0x7fffffff)) = true GAGAL
v = nan, (isunordered (1.2345, value)) = true Success
u = 3,14, (isunordered (1.2345, value)) = false Success
w = inf, (isunordered (1.2345, value)) = false Success
v = nan, (is_ieee754_nan (value)) = Kesuksesan sejati
u = 3,14, (is_ieee754_nan (nilai)) = Keberhasilan palsu
w = inf, (is_ieee754_nan (value)) = Keberhasilan palsu
[C: \ my \ forum \ so \ 282 (deteksi NaN)]
> _
Meringkas hasil di atas, hanya pengujian langsung representasi bit-level, menggunakan is_ieee754_nanfungsi yang didefinisikan dalam program pengujian ini, bekerja dengan andal dalam semua kasus dengan g ++ dan Visual C ++.
Tambahan:
Setelah memposting di atas, saya menyadari kemungkinan lain untuk menguji NaN, yang disebutkan dalam jawaban lain di sini, yaitu ((value < 0) == (value >= 0)). Itu ternyata bekerja dengan baik dengan Visual C ++ tetapi gagal dengan -ffast-mathopsi g ++ . Hanya pengujian bitptern secara langsung yang dapat diandalkan.
inlineboolIsNan(float f){constuint32 u =*(uint32*)&f;return(u&0x7F800000)==0x7F800000&&(u&0x7FFFFF);// Both NaN and qNan.}inlineboolIsNan(double d){constuint64 u =*(uint64*)&d;return(u&0x7FF0000000000000ULL)==0x7FF0000000000000ULL&&(u&0xFFFFFFFFFFFFFULL);}
Ini berfungsi jika sizeof(int)4 dan sizeof(long long)8.
Selama waktu berjalan itu hanya perbandingan, coran tidak membutuhkan waktu. Itu hanya mengubah konfigurasi flag perbandingan untuk memeriksa kesetaraan.
Juga perhatikan, terbatas pada representasi IEEE 754.
Ceria dan hth. - Alf
Perhatikan bahwa para pemain ini melanggar aturan aliasing ketat dari g ++, dan bahwa kompiler telah dikenal untuk melakukan Unmentionable Things ™ ketika mendeteksi UB formal. Alih-alih gips efisien, dengan g ++ Anda perlu menggunakan memcpy, melalui array byte untuk memastikan. Kode untuk itu di jawaban # 2 saya .
Ceria dan hth. - Alf
4
Solusi yang mungkin tidak akan bergantung pada representasi IEEE spesifik untuk NaN yang digunakan adalah sebagai berikut:
template<class T>bool isnan( T f ){
T _nan =(T)0.0/(T)0.0;return0== memcmp((void*)&f,(void*)&_nan,sizeof(T));}
Floating point presisi tunggal memiliki lebih dari 8 juta representasi bit yang sah dan berbeda untuk NaN, jadi Anda perlu menambahkan beberapa perbandingan lagi. :)
Steve Hollasch
4
Mempertimbangkan bahwa (x! = X) tidak selalu dijamin untuk NaN (seperti jika menggunakan opsi -fast-matematika), saya telah menggunakan:
#define IS_NAN(x)(((x)<0)==((x)>=0))
Angka tidak bisa keduanya <0 dan> = 0, jadi pemeriksaan ini hanya akan berlalu jika angkanya tidak kurang dari, atau lebih besar dari atau sama dengan nol. Yang pada dasarnya tidak ada angka sama sekali, atau NaN.
Anda juga dapat menggunakan ini jika Anda lebih suka:
#define IS_NAN(x)(!((x)<0)&&!((x)>=0)
Saya tidak yakin bagaimana ini dipengaruhi oleh -Fast-matematika, jadi jarak tempuh Anda dapat bervariasi.
Ini sebenarnya cacat dengan cara yang sama f != fjuga cacat. Saya telah melihat llvm mengoptimalkan sepotong kode yang hampir identik. Pengoptimal dapat menyebarkan informasi tentang perbandingan pertama dan mengetahui bahwa perbandingan kedua mungkin tidak pernah benar jika yang pertama adalah. (jika kompiler secara ketat mematuhi aturan IEEE f != fjauh lebih sederhana pula)
Bagi saya solusinya bisa makro untuk membuatnya secara eksplisit sejajar dan dengan demikian cukup cepat. Ini juga berfungsi untuk semua tipe float. Itu didasarkan pada kenyataan bahwa satu-satunya kasus ketika nilai tidak sama dengan dirinya sendiri adalah ketika nilai bukan angka.
Tampaknya bagi saya bahwa pendekatan lintas platform yang terbaik adalah menggunakan gabungan dan menguji pola bit ganda untuk memeriksa NaN.
Saya belum benar-benar menguji solusi ini, dan mungkin ada cara yang lebih efisien untuk bekerja dengan pola bit, tetapi saya pikir itu harus bekerja.
#include<stdint.h>#include<stdio.h>unionNaN{uint64_t bits;double num;};int main(){//Test if a double is NaNdouble d =0.0/0.0;unionNaN n;
n.num = d;if((n.bits |0x800FFFFFFFFFFFFF)==0xFFFFFFFFFFFFFFFF){
printf("NaN: %f", d);}return0;}
Perhatikan bahwa "itu perilaku yang tidak ditentukan untuk membaca dari anggota serikat yang paling baru ditulis". Jadi penggunaan ini unionuntuk mengetik-pun antara dua jenis mungkin tidak berfungsi seperti yang diinginkan (: sad_panda :). Cara yang benar (walaupun sebenarnya tidak terlalu portabel seperti yang diinginkan) adalah untuk menghindari penyatuan sama sekali, dan melakukan memcpy dari doubleke dalam uint64_tvariabel yang berbeda , kemudian melakukan tes menggunakan variabel pembantu itu.
Eljay
0
Pada x86-64 Anda dapat memiliki metode yang sangat cepat untuk memeriksa NaN dan infinity, yang berfungsi terlepas dari -ffast-mathopsi kompiler. ( f != f, std::isnan, std::isinfSelalu menghasilkan falsedengan -ffast-math).
Pengujian untuk NaN, angka tak terbatas dan terbatas dapat dengan mudah dilakukan dengan memeriksa eksponen maksimum. infinity adalah eksponen maksimum dengan mantissa nol, NaN adalah eksponen maksimum dan mantissa bukan nol. Eksponen disimpan dalam bit berikutnya setelah bit tanda paling atas, sehingga kita bisa meninggalkan shift untuk menyingkirkan bit tanda dan membuat eksponen bit paling atas, tidak ada masking ( operator&) yang diperlukan:
staticinlineuint64_t load_ieee754_rep(double a){uint64_t r;static_assert(sizeof r ==sizeof a,"Unexpected sizes.");
std::memcpy(&r,&a,sizeof a);// Generates movq instruction.return r;}staticinlineuint32_t load_ieee754_rep(float a){uint32_t r;static_assert(sizeof r ==sizeof a,"Unexpected sizes.");
std::memcpy(&r,&a,sizeof a);// Generates movd instruction.return r;}constexpruint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);constexpruint32_t inf_float_shl1 = UINT32_C(0xff000000);// The shift left removes the sign bit. The exponent moves into the topmost bits,// so that plain unsigned comparison is enough.staticinlinebool isnan2(double a){return load_ieee754_rep(a)<<1> inf_double_shl1;}staticinlinebool isinf2(double a){return load_ieee754_rep(a)<<1== inf_double_shl1;}staticinlinebool isfinite2(double a){return load_ieee754_rep(a)<<1< inf_double_shl1;}staticinlinebool isnan2(float a){return load_ieee754_rep(a)<<1> inf_float_shl1;}staticinlinebool isinf2(float a){return load_ieee754_rep(a)<<1== inf_float_shl1;}staticinlinebool isfinite2(float a){return load_ieee754_rep(a)<<1< inf_float_shl1;}
The stdversi isinfdan isfinitebeban 2 double/floatkonstanta dari .datasegmen dan dalam skenario kasus terburuk dapat menyebabkan 2 data cache misses. Versi di atas tidak memuat data apa pun, inf_double_shl1dan inf_float_shl1konstanta disandikan sebagai operan langsung ke instruksi perakitan.
Menggunakan fakta bahwa ucomisdinstruksi menetapkan flag parity jika ada argumen yang NaN. Ini adalah cara std::isnankerjanya ketika tidak ada -ffast-mathopsi yang ditentukan.
Standar IEEE mengatakan ketika eksponen adalah semua 1s dan mantissa tidak nol, angkanya adalah a NaN. Dobel adalah 1tanda bit, 11bit eksponen dan 52bit mantissa. Lakukan sedikit pemeriksaan.
Seperti komentar di atas menyatakan a! = A tidak akan berfungsi di g ++ dan beberapa kompiler lain, tetapi trik ini seharusnya. Ini mungkin tidak seefisien, tapi masih ada cara:
Pada dasarnya, dalam g ++ (saya tidak yakin tentang yang lain) printf mencetak 'nan' pada format% d atau% .f jika variabel bukan integer / float yang valid. Oleh karena itu kode ini memeriksa karakter pertama dari string menjadi 'n' (seperti dalam "nan")
Bukankah itu akan menyebabkan buffer overflow jika a = 234324.0f?
Mazyod
Ya, akan, atau 340282346638528859811704183484516925440.000jika a = FLT_MAX. Dia harus menggunakan char s[7]; sprintf(s, "%.0g", a);, yang akan menjadi 6 chrs jika a=-FLT_MAX, atau-3e+38
bobobobo
-3
Ini mendeteksi ketidakterbatasan dan juga NaN di Visual Studio dengan mengeceknya dalam batas ganda:
//#include <float.h>double x, y =-1.1; x = sqrt(y);if(x >= DBL_MIN && x <= DBL_MAX )
cout <<"DETECTOR-2 of errors FAILS"<< endl;else
cout <<"DETECTOR-2 of errors OK"<< endl;
Periksa definisi FLT_MIN, DBL_MINdan LDBL_MINlebih hati-hati. Ini didefinisikan sebagai nilai normalisasi terkecil untuk setiap jenis. Misalnya, presisi tunggal memiliki lebih dari 8 juta nilai denorm yang sah yang lebih besar dari nol dan kurang dari FLT_MIN(dan bukan NaN).
nan
kode Anda.nan
Hal ini dapat sangat merusak program Anda, jika dibiarkan berkembang biak, itu dapat menyebabkan sulit untuk menemukan bug. Ini karenanan
beracun, (5 *nan
=nan
),nan
tidak sama dengan apa pun (nan
! =nan
),nan
Tidak lebih besar dari apa pun (nan
!> 0),nan
tidak kurang dari apa pun (nan
! <0).Jawaban:
Menurut standar IEEE, nilai NaN memiliki properti ganjil yang perbandingannya selalu salah. Artinya, untuk float f,
f != f
akan benar hanya jika f adalah NaN.Perhatikan bahwa, seperti yang ditunjukkan beberapa komentar di bawah, tidak semua kompiler menghargai ini ketika mengoptimalkan kode.
Untuk kompiler mana pun yang mengklaim menggunakan IEEE floating point, trik ini harus bekerja. Tetapi saya tidak dapat menjamin bahwa itu akan berhasil dalam praktek. Periksa dengan kompiler Anda, jika ragu.
sumber
-ffast-math
opsi secara eksplisit mengatakan bahwa ia dapat menghasilkan output yang salah untuk program yang bergantung pada implementasi yang tepat jika IEEE atau ISO aturan / spesifikasi untuk fungsi matematika. Tanpa opsi itu diaktifkan, menggunakanx != x
adalah cara pengujian yang valid dan portabel untuk NaN.x != x
valid tanpa opsi itu tidak mengikuti secara logis. mungkin benar untuk versi g ++ tertentu, atau tidak. Lagi pula, Anda umumnya tidak memiliki cara untuk menjamin bahwa opsi fastmath tidak akan digunakan.-ffast-math
opsi), maka itux != x
adalah solusi yang valid dan portabel. Anda bahkan dapat menguji-ffast-math
dengan menguji__FAST_MATH__
makro dan beralih ke implementasi yang berbeda dalam kasus itu (misalnya menggunakan serikat pekerja dan sedikit memutar-mutar).Tidak ada
isnan()
fungsi yang tersedia di C ++ Standard Library saat ini. Itu diperkenalkan di C99 dan didefinisikan sebagai makro bukan fungsi. Elemen-elemen pustaka standar yang didefinisikan oleh C99 bukan bagian dari standar C ++ saat ini ISO / IEC 14882: 1998 juga tidak pembaruan ISO / IEC 14882: 2003Pada tahun 2005 Laporan Teknis 1 diusulkan. TR1 menghadirkan kompatibilitas dengan C99 ke C ++. Terlepas dari kenyataan itu tidak pernah secara resmi diadopsi menjadi standar C ++, banyak ( GCC 4.0+ atau Visual C ++ 9.0+ implementasi C ++ memang menyediakan fitur TR1, semuanya atau hanya beberapa (Visual C ++ 9.0 tidak menyediakan fungsi matematika C99) .
Jika TR1 tersedia, maka
cmath
sertakan elemen C99 sepertiisnan()
,,isfinite()
dll. Tetapi mereka didefinisikan sebagai fungsi, bukan makro, biasanya distd::tr1::
namespace, meskipun banyak implementasi (yaitu GCC 4+ di Linux atau di XCode pada Mac OS X 10.5+) menyuntikkannya langsung kestd::
, jadistd::isnan
didefinisikan dengan baik.Selain itu, beberapa implementasi C ++ masih membuat
isnan()
makro C99 tersedia untuk C ++ (termasuk melaluicmath
ataumath.h
), yang dapat menyebabkan lebih banyak kebingungan dan pengembang mungkin menganggapnya sebagai perilaku standar.Catatan tentang Viusal C ++, seperti yang disebutkan di atas, tidak menyediakan
std::isnan
keduanyastd::tr1::isnan
, tetapi menyediakan fungsi ekstensi_isnan()
yang telah tersedia sejak Visual C ++ 6.0Di XCode, ada yang lebih menyenangkan. Seperti yang disebutkan, GCC 4+ mendefinisikan
std::isnan
. Untuk versi yang lebih lama dari kompiler dan bentuk perpustakaan XCode, tampaknya (di sini adalah diskusi yang relevan ), belum memiliki kesempatan untuk memeriksa sendiri) dua fungsi didefinisikan,__inline_isnand()
pada Intel dan__isnand()
pada Power PC.sumber
std::isnan
sekarang merupakan bagian dari standar C ++ 11 dan dukungan telah menyebar. std :: isnan diimplementasikan di Visual Studio dimulai dengan Visual Studio 2013. Mungkin @shuhalo bertanggung jawab :-)Solusi pertama: jika Anda menggunakan C ++ 11
Karena ini ditanyakan ada sedikit perkembangan baru: penting untuk mengetahui bahwa itu
std::isnan()
adalah bagian dari C ++ 11Ringkasan
Didefinisikan di header
<cmath>
Menentukan apakah arg floating point number bukan-a-number (
NaN
).Parameter
arg
: nilai floating pointNilai pengembalian
true
jika arg adalahNaN
,false
jika tidakReferensi
http://en.cppreference.com/w/cpp/numeric/math/isnan
Harap perhatikan bahwa ini tidak kompatibel dengan -fast-math jika Anda menggunakan g ++, lihat saran di bawah ini.
Solusi lain: jika Anda menggunakan alat yang tidak sesuai C ++ 11
Untuk C99, di C, ini diimplementasikan sebagai makro
isnan(c)
yang mengembalikan nilai int. Jenisx
harus mengambang, ganda atau panjang ganda.Berbagai vendor mungkin atau mungkin tidak termasuk atau tidak fungsi
isnan()
.Cara yang seharusnya portabel untuk memeriksa
NaN
adalah dengan menggunakan properti IEEE 754 yangNaN
tidak sama dengan dirinya sendiri: yaitux == x
akan salah untukx
menjadiNaN
.Namun opsi terakhir mungkin tidak berfungsi dengan setiap kompiler dan beberapa pengaturan (terutama pengaturan optimisasi), jadi di pilihan terakhir, Anda selalu dapat memeriksa pola bit ...
sumber
std::isnan
masih merupakan rekomendasi ungood pada Februari 2017, karena tidak bekerja dengan optimalisasi floating point g ++.x != x
danisnan
diharuskan bekerja untuk kepatuhan IEEE 754. Mengenai yang terakhir, standar IEEE 754-2008 menyatakan bahwa "Implementasi harus menyediakan operasi non-komputasi berikut untuk semua format aritmatika yang didukung" dan "isNaN (x) benar jika dan hanya jika x adalah NaN". Untuk memeriksa kesesuaian yang dibutuhkan oleh standaris754version1985()
danis754version2008()
, di mana C ++ menawarkanstd::numeric_limits<Fp>::is_iec559()
(IEC 559 adalah standar yang sama). Sayangnya dengan-ffast-math
optimisasi, mis. G ++ mengklaim kesesuaian tetapi tidak sesuai.Ada juga pustaka header-only yang ada di Boost yang memiliki alat yang rapi untuk menangani tipe data floating point
Anda mendapatkan fungsi-fungsi berikut:
Jika Anda punya waktu maka lihatlah seluruh toolkit Matematika dari Boost, ia memiliki banyak alat yang berguna dan berkembang dengan cepat.
Juga ketika berhadapan dengan floating point dan non-floating point mungkin ide yang baik untuk melihat Konversi Numerik .
sumber
Ada tiga cara "resmi": posix
isnan
makro ,isnan
templat fungsi c ++ 0x , atau_isnan
fungsi visual c ++ .Sayangnya agak tidak praktis untuk mendeteksi mana yang akan digunakan.
Dan sayangnya, tidak ada cara yang dapat diandalkan untuk mendeteksi apakah Anda memiliki representasi IEEE 754 dengan NaNs. Perpustakaan standar menawarkan cara resmi (
numeric_limits<double>::is_iec559
). Tetapi dalam praktiknya kompiler seperti g ++ sekrup itu.Secara teori orang dapat menggunakan secara sederhana
x != x
, tetapi kompiler seperti g ++ dan visual c ++ mengacaukannya.Jadi pada akhirnya, uji untuk bitpatterns NaN tertentu , dengan asumsi (dan semoga menegakkan, di beberapa titik!) Representasi tertentu seperti IEEE 754.
EDIT : sebagai contoh "kompiler seperti g ++ ... mengacaukannya", pertimbangkan
Kompilasi dengan g ++ (TDM-2 mingw32) 4.4.1:
sumber
-ffast-math
opsi secara eksplisit mengatakan bahwa ia dapat menghasilkan output yang salah untuk program yang bergantung pada implementasi yang tepat jika IEEE atau ISO aturan / spesifikasi untuk fungsi matematika. Tanpa opsi itu diaktifkan, menggunakanx != x
adalah cara pengujian yang valid dan portabel untuk NaN.gcc -ffast-math
masih implementasi C ++ yang sesuai (baik, dengan asumsi itunumeric_limits::is_iec559
benar, itu, meskipun Alf menyarankan di atas bahwa tidak): C ++ kode mengandalkan IEEE bukan C ++ portabel dan tidak memiliki hak untuk mengharapkan implementasi untuk menyediakannya.is_iec559
benar dengan-ffast-math
. Jadi masalahnya di sini adalah bahwa dokumen GCC-ffast-math
hanya mengatakan bahwa itu bukan IEEE / ISO untuk fungsi matematika, sedangkan mereka harus mengatakan bahwa itu bukan C ++, karena implementasinyanumeric_limits
borked. Saya kira GCC tidak bisa selalu tahu kapan template didefinisikan, apakah backend akhirnya benar-benar sesuai dengan float, dan bahkan tidak mencoba. IIRC ada masalah serupa dalam daftar bug yang beredar untuk kesesuaian C99 GCC.is_iec559
jika Anda membutuhkan IEEE, dalam praktiknya tampaknya tidak berfungsi pada GCC. C ++ 0x memang memilikiisnan
fungsi, tetapi karena GCC tidak mengimplementasikan dengan benaris_iec559
sekarang, saya kira itu tidak akan di C ++ 0x juga, dan-ffast-math
mungkin akan merusaknyaisnan
.Ada std :: isnan jika Anda kompiler mendukung ekstensi c99, tapi saya tidak yakin apakah mingw melakukannya.
Ini adalah fungsi kecil yang seharusnya berfungsi jika kompiler Anda tidak memiliki fungsi standar:
sumber
isnan
mengembalikan hasil yang salah. Secara teknis benar, kompiler bisa buggy, tetapi dalam praktiknya, Not Gonna Happen. Sama sepertivar != var
. Ia bekerja karena itulah cara nilai-nilai floating point IEEE didefinisikan.Anda dapat menggunakan
numeric_limits<float>::quiet_NaN( )
definisi dilimits
perpustakaan standar untuk mengujinya. Ada konstanta terpisah yang ditentukan untukdouble
.Saya tidak tahu apakah ini berfungsi pada semua platform, karena saya hanya diuji dengan g ++ di Linux.
sumber
Anda dapat menggunakan
isnan()
fungsi ini, tetapi Anda harus menyertakan perpustakaan C matematika.Karena fungsi ini adalah bagian dari C99, maka tidak tersedia di mana-mana. Jika vendor Anda tidak menyediakan fungsi, Anda juga dapat menentukan varian Anda sendiri untuk kompatibilitas.
sumber
isnan
di <math.h>Kode berikut menggunakan definisi NAN (semua bit eksponen yang ditetapkan, setidaknya satu bit bit yang ditetapkan) dan mengasumsikan bahwa sizeof (int) = sizeof (float) = 4. Anda dapat mencari NAN di Wikipedia untuk detailnya.
bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }
sumber
0x7fffffff
hanya akan duduk di memori sebagaiff ff ff 7f
.value
memiliki urutan yang sama seperti halnya0x7f800000
, sehingga semua operasi berbaris (tidak ada pertukaran byte). Saya akan tertarik jika seseorang dapat menguji ini pada platform big endian.0x7fff1234
juga merupakan NaN. Begitu juga0xffffffff
pencegahan nan
Jawaban saya untuk pertanyaan ini adalah jangan gunakan cek surut untuk
nan
. Sebaliknya, gunakan pemeriksaan preventif untuk pembagian formulir0.0/0.0
.nan
hasil dari operasi0.f/0.f
, atau0.0/0.0
.nan
adalah musuh buruk bagi stabilitas kode Anda yang harus dideteksi dan dicegah dengan sangat hati-hati 1 . Propertinan
yang berbeda dari angka normal:nan
beracun, (5 *nan
=nan
)nan
tidak sama dengan apa pun, bahkan tidak sendiri (nan
! =nan
)nan
tidak lebih besar dari apa pun (nan
!> 0)nan
tidak kurang dari apa pun (nan
! <0)2 properti terakhir yang terdaftar adalah kontra-logis dan akan menghasilkan perilaku aneh kode yang bergantung pada perbandingan dengan
nan
angka (properti ke-3 terakhir juga aneh, tetapi Anda mungkin tidak akan pernah melihatx != x ?
dalam kode Anda (kecuali jika Anda memeriksa untuk nan (unreliably))).Dalam kode saya sendiri, saya perhatikan bahwa
nan
nilai cenderung menghasilkan bug yang sulit ditemukan. (Perhatikan bagaimana ini bukan kasus untukinf
atau-inf
. (-inf
<0) mengembalikanTRUE
, (0 <inf
) mengembalikan BENAR, dan bahkan (-inf
<inf
) mengembalikan BENAR. Jadi, dalam pengalaman saya, perilaku kode sering masih seperti yang diinginkan).apa yang harus dilakukan di bawah nan
Apa yang Anda inginkan terjadi di bawah
0.0/0.0
harus ditangani sebagai kasus khusus , tetapi apa yang Anda lakukan harus bergantung pada angka yang Anda harapkan keluar dari kode.Pada contoh di atas, pada dasarnya hasil (
0.f/FLT_MIN
) adalah0
. Anda mungkin ingin0.0/0.0
menghasilkanHUGE
gantinya. Begitu,Jadi di atas, jika x adalah
0.f
,inf
akan menghasilkan (yang memiliki perilaku cukup baik / tidak rusak seperti yang disebutkan di atas sebenarnya).Ingat, pembagian integer dengan 0 menyebabkan pengecualian runtime . Jadi Anda harus selalu memeriksa pembagian integer dengan 0. Hanya karena dengan
0.0/0.0
tenang mengevaluasinan
tidak berarti Anda bisa malas dan tidak memeriksa0.0/0.0
sebelum itu terjadi.1 Pemeriksaan
nan
viax != x
terkadang tidak dapat diandalkan (x != x
dilucuti oleh beberapa kompiler pengoptimal yang merusak kepatuhan IEEE, khususnya ketika-ffast-math
sakelar diaktifkan).sumber
Pada C ++ 14 ada beberapa cara untuk menguji apakah angka floating point
value
adalah NaN.Dari cara-cara ini, hanya memeriksa bit representasi angka, bekerja dengan andal, seperti yang disebutkan dalam jawaban asli saya. Khususnya,
std::isnan
dan pemeriksaan yang sering diajukanv != v
, tidak bekerja dengan andal dan tidak boleh digunakan, jangan sampai kode Anda berhenti bekerja dengan benar ketika seseorang memutuskan bahwa optimasi floating point diperlukan, dan meminta kompiler untuk melakukan itu. Situasi ini dapat berubah, kompiler bisa mendapatkan lebih banyak menyesuaikan diri, tetapi untuk masalah ini yang belum terjadi dalam 6 tahun sejak jawaban asli.Selama sekitar 6 tahun, jawaban awal saya adalah solusi yang dipilih untuk pertanyaan ini, yang OK. Tetapi baru-baru ini jawaban yang sangat tervotasikan merekomendasikan
v != v
tes tidak dapat diandalkan telah dipilih. Oleh karena itu, ini jawaban tambahan yang lebih baru (kami sekarang memiliki standar C ++ 11 dan C ++ 14, dan C ++ 17 di cakrawala).Cara utama untuk memeriksa NaN-ness, pada C ++ 14, adalah:
std::isnan(value) )
adalah cara pustaka standar yang dimaksudkan sejak C ++ 11.
isnan
tampaknya bertentangan dengan makro Posix dengan nama yang sama, tetapi dalam praktiknya itu tidak masalah. Masalah utama adalah bahwa ketika optimasi aritmatika floating point diminta, maka dengan setidaknya satu kompiler utama, yaitu g ++,std::isnan
kembalifalse
untuk argumen NaN .(fpclassify(value) == FP_NAN) )
Menderita masalah yang sama seperti
std::isnan
, yaitu, tidak dapat diandalkan.(value != value) )
Direkomendasikan dalam banyak jawaban SO. Menderita masalah yang sama seperti
std::isnan
, yaitu, tidak dapat diandalkan.(value == Fp_info::quiet_NaN()) )
Ini adalah tes yang dengan perilaku standar tidak boleh mendeteksi NaN, tetapi bahwa dengan perilaku yang dioptimalkan mungkin bisa mendeteksi NaN (karena kode yang dioptimalkan hanya membandingkan representasi bitlevel secara langsung), dan mungkin dikombinasikan dengan cara lain untuk mencakup perilaku standar yang tidak dioptimalkan. , andal bisa mendeteksi NaN. Sayangnya ternyata tidak berfungsi dengan baik.
(ilogb(value) == FP_ILOGBNAN) )
Menderita masalah yang sama seperti
std::isnan
, yaitu, tidak dapat diandalkan.isunordered(1.2345, value) )
Menderita masalah yang sama seperti
std::isnan
, yaitu, tidak dapat diandalkan.is_ieee754_nan( value ) )
Ini bukan fungsi standar. Ini memeriksa bit sesuai dengan standar IEEE 754. Ini benar-benar dapat diandalkan tetapi kode ini agak bergantung pada sistem.
Dalam kode uji lengkap berikut "sukses" adalah apakah suatu ekspresi melaporkan Nannnes dari nilai tersebut. Untuk sebagian besar ekspresi ukuran keberhasilan ini, tujuan mendeteksi NaN dan hanya NaN, sesuai dengan semantik standar mereka. Untuk
(value == Fp_info::quiet_NaN()) )
ekspresi, namun, perilaku standar adalah bahwa hal itu tidak bekerja sebagai NaN-detektor.Hasil dengan g ++ (perhatikan lagi bahwa perilaku standar
(value == Fp_info::quiet_NaN())
adalah bahwa itu tidak berfungsi sebagai detektor NaN, itu hanya sangat menarik di sini):Hasil dengan Visual C ++:
Meringkas hasil di atas, hanya pengujian langsung representasi bit-level, menggunakan
is_ieee754_nan
fungsi yang didefinisikan dalam program pengujian ini, bekerja dengan andal dalam semua kasus dengan g ++ dan Visual C ++.Tambahan:
Setelah memposting di atas, saya menyadari kemungkinan lain untuk menguji NaN, yang disebutkan dalam jawaban lain di sini, yaitu
((value < 0) == (value >= 0))
. Itu ternyata bekerja dengan baik dengan Visual C ++ tetapi gagal dengan-ffast-math
opsi g ++ . Hanya pengujian bitptern secara langsung yang dapat diandalkan.sumber
Ini berfungsi jika
sizeof(int)
4 dansizeof(long long)
8.Selama waktu berjalan itu hanya perbandingan, coran tidak membutuhkan waktu. Itu hanya mengubah konfigurasi flag perbandingan untuk memeriksa kesetaraan.
sumber
memcpy
, melalui array byte untuk memastikan. Kode untuk itu di jawaban # 2 saya .Solusi yang mungkin tidak akan bergantung pada representasi IEEE spesifik untuk NaN yang digunakan adalah sebagai berikut:
sumber
Mempertimbangkan bahwa (x! = X) tidak selalu dijamin untuk NaN (seperti jika menggunakan opsi -fast-matematika), saya telah menggunakan:
Angka tidak bisa keduanya <0 dan> = 0, jadi pemeriksaan ini hanya akan berlalu jika angkanya tidak kurang dari, atau lebih besar dari atau sama dengan nol. Yang pada dasarnya tidak ada angka sama sekali, atau NaN.
Anda juga dapat menggunakan ini jika Anda lebih suka:
Saya tidak yakin bagaimana ini dipengaruhi oleh -Fast-matematika, jadi jarak tempuh Anda dapat bervariasi.
sumber
f != f
juga cacat. Saya telah melihat llvm mengoptimalkan sepotong kode yang hampir identik. Pengoptimal dapat menyebarkan informasi tentang perbandingan pertama dan mengetahui bahwa perbandingan kedua mungkin tidak pernah benar jika yang pertama adalah. (jika kompiler secara ketat mematuhi aturan IEEEf != f
jauh lebih sederhana pula)-ffast-math
opsi g ++ . Bekerja dengan Visual C ++. Lihat ( stackoverflow.com/a/42138465/464581 ).Bagi saya solusinya bisa makro untuk membuatnya secara eksplisit sejajar dan dengan demikian cukup cepat. Ini juga berfungsi untuk semua tipe float. Itu didasarkan pada kenyataan bahwa satu-satunya kasus ketika nilai tidak sama dengan dirinya sendiri adalah ketika nilai bukan angka.
sumber
Ini bekerja:
keluaran: isnan
sumber
Tampaknya bagi saya bahwa pendekatan lintas platform yang terbaik adalah menggunakan gabungan dan menguji pola bit ganda untuk memeriksa NaN.
Saya belum benar-benar menguji solusi ini, dan mungkin ada cara yang lebih efisien untuk bekerja dengan pola bit, tetapi saya pikir itu harus bekerja.
sumber
union
untuk mengetik-pun antara dua jenis mungkin tidak berfungsi seperti yang diinginkan (: sad_panda :). Cara yang benar (walaupun sebenarnya tidak terlalu portabel seperti yang diinginkan) adalah untuk menghindari penyatuan sama sekali, dan melakukan memcpy daridouble
ke dalamuint64_t
variabel yang berbeda , kemudian melakukan tes menggunakan variabel pembantu itu.Pada x86-64 Anda dapat memiliki metode yang sangat cepat untuk memeriksa NaN dan infinity, yang berfungsi terlepas dari
-ffast-math
opsi kompiler. (f != f
,std::isnan
,std::isinf
Selalu menghasilkanfalse
dengan-ffast-math
).Pengujian untuk NaN, angka tak terbatas dan terbatas dapat dengan mudah dilakukan dengan memeriksa eksponen maksimum. infinity adalah eksponen maksimum dengan mantissa nol, NaN adalah eksponen maksimum dan mantissa bukan nol. Eksponen disimpan dalam bit berikutnya setelah bit tanda paling atas, sehingga kita bisa meninggalkan shift untuk menyingkirkan bit tanda dan membuat eksponen bit paling atas, tidak ada masking (
operator&
) yang diperlukan:The
std
versiisinf
danisfinite
beban 2double/float
konstanta dari.data
segmen dan dalam skenario kasus terburuk dapat menyebabkan 2 data cache misses. Versi di atas tidak memuat data apa pun,inf_double_shl1
daninf_float_shl1
konstanta disandikan sebagai operan langsung ke instruksi perakitan.Lebih cepat
isnan2
hanya 2 instruksi perakitan:Menggunakan fakta bahwa
ucomisd
instruksi menetapkan flag parity jika ada argumen yang NaN. Ini adalah carastd::isnan
kerjanya ketika tidak ada-ffast-math
opsi yang ditentukan.sumber
Standar IEEE mengatakan ketika eksponen adalah semua
1
s dan mantissa tidak nol, angkanya adalah aNaN
. Dobel adalah1
tanda bit,11
bit eksponen dan52
bit mantissa. Lakukan sedikit pemeriksaan.sumber
Seperti komentar di atas menyatakan a! = A tidak akan berfungsi di g ++ dan beberapa kompiler lain, tetapi trik ini seharusnya. Ini mungkin tidak seefisien, tapi masih ada cara:
Pada dasarnya, dalam g ++ (saya tidak yakin tentang yang lain) printf mencetak 'nan' pada format% d atau% .f jika variabel bukan integer / float yang valid. Oleh karena itu kode ini memeriksa karakter pertama dari string menjadi 'n' (seperti dalam "nan")
sumber
340282346638528859811704183484516925440.000
jika a =FLT_MAX
. Dia harus menggunakanchar s[7]; sprintf(s, "%.0g", a);
, yang akan menjadi 6 chrs jikaa=-FLT_MAX
, atau-3e+38
Ini mendeteksi ketidakterbatasan dan juga NaN di Visual Studio dengan mengeceknya dalam batas ganda:
sumber
FLT_MIN
,DBL_MIN
danLDBL_MIN
lebih hati-hati. Ini didefinisikan sebagai nilai normalisasi terkecil untuk setiap jenis. Misalnya, presisi tunggal memiliki lebih dari 8 juta nilai denorm yang sah yang lebih besar dari nol dan kurang dariFLT_MIN
(dan bukan NaN).