C ++ 14'ten itibaren bir kayan nokta sayısının test edilip edilmediğini test etmenin birkaç yolu vardır. value
NaN .
Bu yollardan, sadece sayının temsilinin bitlerinin kontrol edilmesi, orijinal cevabımda belirtildiği gibi güvenilir bir şekilde çalışır. Özellikle std::isnan
ve sıkça önerilen çekv != v
, güvenilir bir şekilde çalışmaz ve kullanılmamalıdır, eğer birisi kayan nokta optimizasyonunun gerekli olduğuna karar verdiğinde kodunuz düzgün çalışmayı durdurmaz ve derleyiciden bunu yapmasını ister. Bu durum değişebilir, derleyiciler daha uygun olabilir, ancak bu sorunun orijinal cevabından bu yana 6 yıl içinde gerçekleşmedi.
Yaklaşık 6 yıl boyunca asıl cevabım bu soru için seçilen çözümdü, ki bu iyiydi. Ancak son zamanlarda, güvenilir olmayan v != v
testi öneren oldukça güncel bir cevap seçildi. Dolayısıyla bu ek daha güncel cevap (artık C ++ 11 ve C ++ 14 standartlarına ve ufukta C ++ 17'ye sahibiz).
C ++ 14'ten itibaren NaN-ness'i kontrol etmenin ana yolları şunlardır:
std::isnan(value) )
C ++ 11'den beri istenen standart kütüphane yoludur. isnan
Görünüşe göre aynı adı taşıyan Posix makrosu ile çelişiyor, ancak pratikte bu bir sorun değil. Ana sorun, kayan nokta aritmetik optimizasyonu istendiğinde, en az bir ana derleyici, yani g ++ ile NaN argümanı için std::isnan
geri false
dönmesidir .
(fpclassify(value) == FP_NAN) )
std::isnan
Yani,
aynı problemden muzdarip değildir, yani güvenilir değildir.
(value != value) )
Birçok SO cevabında önerilir. std::isnan
Yani, aynı problemden muzdarip değildir, yani güvenilir değildir.
(value == Fp_info::quiet_NaN()) )
Bu, standart davranışı olan NaN'leri algılamaması gereken bir testtir, ancak optimize edilmiş davranışla NaN'leri algılayabilir (optimize edilmiş kod sadece bit seviyesi sunumlarını doğrudan karşılaştırarak) ve belki de standart optimize edilmemiş davranışı kapsayacak başka bir yolla birleştirilebilir. , NaN'yi güvenilir bir şekilde tespit edebilir. Ne yazık ki güvenilir bir şekilde çalışmadığı ortaya çıktı.
(ilogb(value) == FP_ILOGBNAN) )
std::isnan
Yani,
aynı problemden muzdarip değildir, yani güvenilir değildir.
isunordered(1.2345, value) )
std::isnan
Yani,
aynı problemden muzdarip değildir, yani güvenilir değildir.
is_ieee754_nan( value ) )
Bu standart bir işlev değil. Uçları IEEE 754 standardına göre kontrol ediyor. Tamamen güvenilirdir ancak kod bir şekilde sisteme bağlıdır.
Aşağıdaki tam test kodunda “başarı” ifadenin değerin Naneliğini rapor edip etmediğidir. Çoğu ifade için bu başarı ölçüsü, NaN'leri ve sadece NaN'leri tespit etme amacı, standart anlamlarına karşılık gelir. İçin (value == Fp_info::quiet_NaN()) )
ekspresyon Ancak standart davranışı, NaN-dedektörü olarak çalışmaz olmasıdır.
#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 ) );
}
G ++ ile elde edilen sonuçlar (bunun standart davranışının (value == Fp_info::quiet_NaN())
bir NaN-detektörü olarak çalışmadığına dikkat edin, buradaki pratik ilgi çok fazladır):
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> g ++ - sürüm | "++" bul
g ++ (x86_64-win32-sjlj-rev1, MinGW-W64 projesi tarafından oluşturulmuştur) 6.3.0
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> g ++ foo.cpp && a
Derleyici hak talepleri IEEE 754 = doğru
v = nan, (std :: isnan (değer)) = gerçek Başarı
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı
v = nan, ((fpclassify (değer) == 0x0100)) = gerçek Başarı
u = 3.14, ((fpclassify (değer) == 0x0100)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 0x0100)) = yanlış Başarı
v = nan, ((değer! = değer)) = gerçek Başarı
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı
v = nan, ((değer == Fp_info :: quiet_NaN ())) = yanlış FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
w = inf, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
v = nan, ((ilogb (değer) == ((int) 0x80000000))) = gerçek Başarı
u = 3.14, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı
w = inf, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı
v = nan, (değersiz (1.2345, değer)) = gerçek Başarı
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı
v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> g ++ foo.cpp -ffast-math && a
Derleyici hak talepleri IEEE 754 = doğru
v = nan, (std :: isnan (değer)) = yanlış FAILED
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı
v = nan, ((fpclassify (değer) == 0x0100)) = yanlış FAILED
u = 3.14, ((fpclassify (değer) == 0x0100)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 0x0100)) = yanlış Başarı
v = nan, ((değer! = değer)) = yanlış FAILED
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı
v = nan, ((value == Fp_info :: quiet_NaN ())) = gerçek Başarı
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = doğru BAŞARISIZ
w = inf, ((value == Fp_info :: quiet_NaN ())) = doğru BAŞARISIZ
v = nan, ((ilogb (değer) == ((int) 0x80000000))) = gerçek Başarı
u = 3.14, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı
w = inf, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı
v = nan, (ayrıştırılmadı (1.2345, değer)) = yanlış FAILED
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı
v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> _
Visual C ++ ile sonuçlar:
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> cl / nologo- 2> & 1 | "++" bul
X86 için Microsoft (R) C / C ++ Optimize Edici Derleyici Sürümü 19.00.23725
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> cl foo.cpp / Şub && b
foo.cpp
Derleyici hak talepleri IEEE 754 = doğru
v = nan, (std :: isnan (değer)) = gerçek Başarı
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı
v = nan, ((fpclassify (değer) == 2)) = gerçek Başarı
u = 3.14, ((fpclassify (değer) == 2)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 2)) = yanlış Başarı
v = nan, ((değer! = değer)) = gerçek Başarı
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı
v = nan, ((değer == Fp_info :: quiet_NaN ())) = yanlış FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
w = inf, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
v = nan, ((ilogb (değer) == 0x7fffffff)) = gerçek Başarı
u = 3.14, ((ilogb (değer) == 0x7fffffff)) = yanlış Başarı
w = inf, ((ilogb (değer) == 0x7fffffff)) = gerçek BAŞARISIZ
v = nan, (değersiz (1.2345, değer)) = gerçek Başarı
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı
v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> cl foo.cpp / Şub / fp: hızlı && b
foo.cpp
Derleyici hak talepleri IEEE 754 = doğru
v = nan, (std :: isnan (değer)) = gerçek Başarı
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı
v = nan, ((fpclassify (değer) == 2)) = gerçek Başarı
u = 3.14, ((fpclassify (değer) == 2)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 2)) = yanlış Başarı
v = nan, ((değer! = değer)) = gerçek Başarı
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı
v = nan, ((değer == Fp_info :: quiet_NaN ())) = yanlış FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
w = inf, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
v = nan, ((ilogb (değer) == 0x7fffffff)) = gerçek Başarı
u = 3.14, ((ilogb (değer) == 0x7fffffff)) = yanlış Başarı
w = inf, ((ilogb (değer) == 0x7fffffff)) = gerçek BAŞARISIZ
v = nan, (değersiz (1.2345, değer)) = gerçek Başarı
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı
v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı
[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> _
Yukarıdaki sonuçları özetlemek gerekirse is_ieee754_nan
, bu test programında tanımlanan işlevi kullanarak sadece bit seviyesi gösteriminin doğrudan test edilmesi, hem g ++ hem de Visual C ++ ile her durumda güvenilir bir şekilde çalıştı.
Zeyilname:
Yukarıdakileri gönderdikten sonra, buradaki başka bir cevapta bahsedilen NaN'yi test etmek için bir başka ihtimal olduğunun farkına vardım ((value < 0) == (value >= 0))
. Bu Visual C ++ ile iyi çalıştığı ortaya çıktı ama g ++ 'ın -ffast-math
seçeneği ile başarısız oldu . Sadece doğrudan bitpattern testi güvenilir bir şekilde çalışır.