Memeriksa apakah double (atau float) adalah NaN dalam C ++

369

Apakah ada fungsi isnan ()?

PS .: Saya di MinGW (jika itu membuat perbedaan).

Saya menyelesaikan masalah ini dengan menggunakan isnan () dari <math.h>, yang tidak ada di <cmath>dalamnya, yang saya gunakan #includepada awalnya.

Hasen
sumber
2
Saya tidak murni Anda bisa melakukannya dengan mudah. Siapa bilang C ++ membutuhkan IEEE754?
David Heffernan
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.

jalf
sumber
4
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.

mloskot
sumber
21
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

Ringkasan

Didefinisikan di header <cmath>

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

Menentukan apakah arg floating point number bukan-a-number ( NaN).

Parameter

arg: nilai floating point

Nilai pengembalian

truejika arg adalah NaN, falsejika tidak

Referensi

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

BlueTrin
sumber
8
Jelas layak menjadi jawaban yang diterima dan layak mendapat lebih banyak upvotes. Terima kasih atas tipnya
LBes
3
−1 std::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

#include <boost/math/special_functions/fpclassify.hpp>

Anda mendapatkan fungsi-fungsi berikut:

template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);

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 .

Anonim
sumber
1
Terima kasih! Apa yang saya cari.
Dr. Watson
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;
    double const nan1 = Info::quiet_NaN();
    double const 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> _
Ceria dan hth. - Alf
sumber
4
@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)
{
    volatile double d = var;
    return d != d;
}
CTT
sumber
6
mengapa tidak hanya var! = var?
Brian R. Bondy
8
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>

using namespace 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;
   }

   return 0;
}

Saya tidak tahu apakah ini berfungsi pada semua platform, karena saya hanya diuji dengan g ++ di Linux.

Bill the Lizard
sumber
2
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.

inline bool isnan(double x) {
    return x != x;
}
raimue
sumber
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.

bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }

Ian
sumber
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.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) 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 possible
else
  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).

bobobobo
sumber
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::isnan kembali 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.

#include <cmath>        // std::isnan, std::fpclassify
#include <iostream>
#include <iomanip>      // std::setw
#include <limits>
#include <limits.h>     // CHAR_BIT
#include <sstream>
#include <stdint.h>     // uint64_t
using namespace std;

#define TEST( x, expr, expected ) \
    [&](){ \
        const auto value = x; \
        const bool result = expr; \
        ostringstream stream; \
        stream << boolalpha << #x " = " << x << ", (" #expr ") = " << result; \
        cout \
            << setw( 60 ) << stream.str() << "  " \
            << (result == expected? "Success" : "FAILED") \
            << endl; \
    }()

#define TEST_ALL_VARIABLES( expression ) \
    TEST( v, expression, true ); \
    TEST( u, expression, false ); \
    TEST( w, expression, false )

using Fp_info = numeric_limits<double>;

inline auto is_ieee754_nan( double const x )
    -> bool
{
    static constexpr bool   is_claimed_ieee754  = Fp_info::is_iec559;
    static constexpr int    n_bits_per_byte     = CHAR_BIT;
    using Byte = unsigned char;

    static_assert( is_claimed_ieee754, "!" );
    static_assert( n_bits_per_byte == 8, "!" );
    static_assert( sizeof( x ) == sizeof( uint64_t ), "!" );

    #ifdef _MSC_VER
        uint64_t const bits = reinterpret_cast<uint64_t const&>( x );
    #else
        Byte bytes[sizeof(x)];
        memcpy( bytes, &x, sizeof( x ) );
        uint64_t int_value;
        memcpy( &int_value, bytes, sizeof( x ) );
        uint64_t const& bits = int_value;
    #endif

    static constexpr uint64_t   sign_mask       = 0x8000000000000000;
    static constexpr uint64_t   exp_mask        = 0x7FF0000000000000;
    static constexpr uint64_t   mantissa_mask   = 0x000FFFFFFFFFFFFF;

    (void) sign_mask;
    return (bits & exp_mask) == exp_mask and (bits & mantissa_mask) != 0;
}

auto main()
    -> int
{
    double const v = Fp_info::quiet_NaN();
    double const u = 3.14;
    double const w = Fp_info::infinity();

    cout << boolalpha << left;
    cout << "Compiler claims IEEE 754 = " << Fp_info::is_iec559 << endl;
    cout << endl;;
    TEST_ALL_VARIABLES( std::isnan(value) );                    cout << endl;
    TEST_ALL_VARIABLES( (fpclassify(value) == FP_NAN) );        cout << endl;
    TEST_ALL_VARIABLES( (value != value) );                     cout << endl;
    TEST_ALL_VARIABLES( (value == Fp_info::quiet_NaN()) );      cout << endl;
    TEST_ALL_VARIABLES( (ilogb(value) == FP_ILOGBNAN) );        cout << endl;
    TEST_ALL_VARIABLES( isunordered(1.2345, value) );           cout << endl;
    TEST_ALL_VARIABLES( is_ieee754_nan( value ) );
}

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.

Ceria dan hth. - Alf
sumber
7
inline bool IsNan(float f)
{
    const uint32 u = *(uint32*)&f;
    return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF);    // Both NaN and qNan.
}

inline bool IsNan(double d)
{
    const uint64 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.

ST3
sumber
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;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
Dan Nathan
sumber
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.

Jerramy
sumber
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)
Markus
Tidak bekerja dengan -ffast-mathopsi g ++ . Bekerja dengan Visual C ++. Lihat ( stackoverflow.com/a/42138465/464581 ).
Ceria dan hth. - Alf
3

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.

#ifndef isnan
  #define isnan(a) (a != a)
#endif
pengguna1705817
sumber
Ini adalah salah satu jawaban terbaik untuk pertanyaan ini! Terima kasih sudah berbagi.
Henri Menke
2
Jawaban lain menunjukkan bahwa ini bisa gagal dengan opsi -fast-matematika yang disetel.
Technophile
3

Ini bekerja:

#include <iostream>
#include <math.h>
using namespace std;

int main ()
{
  char ch='a';
  double val = nan(&ch);
  if(isnan(val))
     cout << "isnan" << endl;

  return 0;
}

keluaran: isnan

edW
sumber
1

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>

union NaN
{
    uint64_t bits;
    double num;
};

int main()
{
    //Test if a double is NaN
    double d = 0.0 / 0.0;
    union NaN n;
    n.num = d;
    if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
    {
        printf("NaN: %f", d);
    }

    return 0;
}
Sheldon Juncker
sumber
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:

static inline uint64_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;
}

static inline uint32_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;
}

constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_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.
static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool 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.


Lebih cepat isnan2hanya 2 instruksi perakitan:

bool isnan2(double a) {
    bool r;
    asm(".intel_syntax noprefix"
        "\n\t ucomisd %1, %1"
        "\n\t setp %b0"
        "\n\t .att_syntax prefix"
        : "=g" (r)
        : "x" (a)
        : "cc"
        );
    return r;
}

Menggunakan fakta bahwa ucomisdinstruksi menetapkan flag parity jika ada argumen yang NaN. Ini adalah cara std::isnankerjanya ketika tidak ada -ffast-mathopsi yang ditentukan.

Maxim Egorushkin
sumber
-1

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.

memukul
sumber
-3

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:

bool IsNan(float a)
{
    char s[4];
    sprintf(s, "%.3f", a);
    if (s[0]=='n') return true;
    else return false;
}

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")

ZenJ
sumber
2
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;
ahli matematika
sumber
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).
Steve Hollasch