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 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 != 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
kembali false
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.
#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_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.