Checking if a double (or float) is NaN in C++
有没有在isnan()函数?
PS:如果我让一个差分)。
这是我从
根据IEEE标准,NaN值具有奇怪的特性,涉及它们的比较总是错误的。也就是说,对于浮点数f,只有当f为NaN时,
注意,正如下面的一些注释所指出的,并不是所有编译器在优化代码时都会尊重这一点。
对于声称使用IEEEfloatingpoint的任何编译器来说,这个技巧都是有效的。但我不能保证它会在实践中起作用。如果有疑问,请与编译器联系。
当前C++标准库中没有可用的EDCOX1 1函数。它是在c99中引入的,定义为宏而不是函数。C99定义的标准库的元素不是当前的C++标准ISO/IEC 1488∶1998的一部分,而不是其更新ISO/IEC 1488∶2003。
2005年提出了技术报告1。Tr1带来了与C99到C++的兼容性。尽管它从未被正式采纳成为C++标准,许多(GCC 4 +或Visual C++ 9+C++实现确实提供了Tr1特征,它们全部或仅一些(Visual C++ 9不提供C99数学函数)。
如果tr1可用,那么
此外,C++的一些实现仍然使C99 EDCOX1和1的宏可用于C++(包括通过EDCOX1、6或EDCOX1×14),这可能会造成更多混乱,开发人员可能会认为这是一种标准行为。
关于viiC++的注释,如上所述,它不提供EDCOX1〔11〕既不EDCOX1也不16,但它提供了一个扩展函数,定义为EDCOX1,17,这是自从Visual C++ 6以来可用的。
在Xcode上,还有更多的乐趣。如前所述,GCC4+定义了
第一个解决方案:如果你使用C++ 11
因为这被问到有一些新的发展:重要的是知道EDCOX1的6个部分是C++ 11的一部分。
简介在标题
1 2 3 | bool isnan( float arg ); (since C++11) bool isnan( double arg ); (since C++11) bool isnan( long double arg ); (since C++11) |
确定给定的浮点数arg是否不是数字(
参数
返回值
如果arg为
参考文献
http://en.cppreference.com/w/cpp/numeric/math/isnan
请注意,这与-fast math不兼容,如果您使用g++,请参阅下面的其他建议。
其他解决方案:如果使用非C++ 11兼容工具对于c99,在c中,这是作为返回int值的宏
不同的供应商可能包括也可能不包括一个函数
假设检查
但是,最后一个选项可能无法与每个编译器和某些设置(特别是优化设置)一起使用,因此,在最后一个方法中,您可以始终检查位模式…
Boost中还存在一个只包含头的库,它具有处理浮点数据类型的简洁工具。
1 | #include <boost/math/special_functions/fpclassify.hpp> |
您可以获得以下功能:
1 2 3 4 | 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); |
如果你有时间看一下Boost的整个数学工具包,它有许多有用的工具,并且正在快速增长。
同样,在处理浮点数和非浮点数时,最好查看数值转换。
有三种"官方"方式:POSIX EDCOX1,0宏,C++0X EDCOX1,0函数模板,或Visual C++ EDCOX1×2函数。
不幸的是,检测使用哪种方法是相当不切实际的。
不幸的是,没有可靠的方法来检测您是否有NAN的IEEE754表示。标准图书馆提供了一种官方的方式(
理论上,可以简单地使用EDCOX1,4,但是编译器如G++和VisualC++将其放大。
因此,在最后,测试特定的NAN位模式,假设(并且希望在某个时刻强制执行!)一种特殊的表示法,如IEEE754。
编辑:作为"编译器如g++&hellip;搞砸它"的一个例子,考虑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <limits> #include 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 ); } |
用G++编译(TDM-2 mingW32)4.4.1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | C:\test> type"C:\Program Files\@commands\gnuc.bat" @rem -finput-charset=windows-1252 @g++ -O -pedantic -std=c++98 -Wall -Wwrite-strings %* -Wno-long-long C:\test> gnuc x.cpp C:\test> a && echo works... || echo !failed works... C:\test> gnuc x.cpp --fast-math C:\test> a && echo works... || echo !failed Assertion failed: a != b, file x.cpp, line 6 This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. !failed C:\test> _ |
有一个STD::ISNN,如果编译器支持C99扩展,但我不知道是否MIW。
下面是一个小函数,如果您的编译器没有标准函数,它应该可以工作:
1 2 3 4 5 | bool custom_isnan(double var) { volatile double d = var; return d != d; } |
可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #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; } |
我不知道这是否适用于所有平台,因为我只在Linux上用g++测试过。
您可以使用
1 | #include <cmath> |
由于此功能是C99的一部分,因此并非所有地方都可用。如果您的供应商不提供该函数,您还可以为兼容性定义自己的变体。
1 2 3 | inline bool isnan(double x) { return x != x; } |
南防
我对这个问题的回答是不要对
1 2 3 4 5 | #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. |
nan 有毒,(5*nan =nan )nan 不等于任何东西,甚至不等于本身(nan !=nan )nan 不大于任何(EDOCX1〔0)!> 0)nan 不小于任何东西(EDOCX1〔0)!< 0)
所列的最后2个属性是反逻辑的,将导致代码行为异常,依赖于与
在我自己的代码中,我注意到
在
在上面的例子中,(
1 2 3 4 5 | 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. |
因此,在上述情况下,如果x为
记住,整数除以0会导致运行时异常。所以必须始终检查整数除以0。仅仅因为
1通过
以下代码使用NaN(所有指数位集,至少一个小数位集)的定义,并假定sizeof(int)=sizeof(float)=4。你可以在维基百科上查找Nan的详细信息。
{
return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000;
}
对于C++ 14,有很多方法来测试浮点数EDOCX1·0是一个Na。
在这些方法中,只检查数字表示的位,如我最初的答案所述,工作可靠。特别是,
在大约6年的时间里,我最初的答案是这个问题的解决方案,这是可以的。但最近,一个高度乐观的建议不可靠的
检查C++的14个主要方法是:
std::isnan(value) ) 是C++ 11以来的标准库方式。isnan 显然与同名的posix宏,但实际上这不是问题。主要问题是当请求浮点算术优化时,那么至少有一个主编译器,即g++,std::isnan 为nan参数返回false 。8与
std::isnan 的问题相同,即不可靠。(value != value) ) 在许多这样的答案中推荐。与std::isnan 的问题相同,即:不可靠。(value == Fp_info::quiet_NaN()) ) 这是一个具有标准行为的测试,不应检测到nan,而是使用优化的行为可能会检测到nan(由于优化的代码只是比较直接进行位级表示),并可能与另一种覆盖标准未优化行为,能可靠检测到NaN。不幸地结果证明它不能可靠地工作。(ilogb(value) == FP_ILOGBNAN) ) 与std::isnan 的问题相同,即不可靠。isunordered(1.2345, value) ) 与std::isnan 的问题相同,即不可靠。is_ieee754_nan( value ) ) 这不是标准功能。它是根据IEEE754检查位的。标准。它是完全可靠的,但代码在某种程度上依赖于系统。
在以下完整的测试代码中,"Success"是一个表达式是否报告了值的Nanness。对于大多数表达式来说,这种成功度量的目标是检测nan,而仅检测nan,这与它们的标准语义相对应。然而,对于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | #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++的结果(再次注意,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | [C:\my\forums\so\282 (detect NaN)] > g++ --version | find"++" g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0 [C:\my\forums\so\282 (detect NaN)] > g++ foo.cpp && a Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 0x0100)) = true Success u = 3.14, ((fpclassify(value) == 0x0100)) = false Success w = inf, ((fpclassify(value) == 0x0100)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == ((int)0x80000000))) = true Success u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false Success w = inf, ((ilogb(value) == ((int)0x80000000))) = false Success 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 )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > g++ foo.cpp -ffast-math && a Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = false FAILED u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 0x0100)) = false FAILED u = 3.14, ((fpclassify(value) == 0x0100)) = false Success w = inf, ((fpclassify(value) == 0x0100)) = false Success v = nan, ((value != value)) = false FAILED u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = true Success u = 3.14, ((value == Fp_info::quiet_NaN())) = true FAILED w = inf, ((value == Fp_info::quiet_NaN())) = true FAILED v = nan, ((ilogb(value) == ((int)0x80000000))) = true Success u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false Success w = inf, ((ilogb(value) == ((int)0x80000000))) = false Success v = nan, (isunordered(1.2345, value)) = false FAILED u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > _ |
Visual C++的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | [C:\my\forums\so\282 (detect NaN)] > cl /nologo- 2>&1 | find"++" Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86 [C:\my\forums\so\282 (detect NaN)] > cl foo.cpp /Feb && b foo.cpp Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 2)) = true Success u = 3.14, ((fpclassify(value) == 2)) = false Success w = inf, ((fpclassify(value) == 2)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == 0x7fffffff)) = true Success u = 3.14, ((ilogb(value) == 0x7fffffff)) = false Success w = inf, ((ilogb(value) == 0x7fffffff)) = true FAILED 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 )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > cl foo.cpp /Feb /fp:fast && b foo.cpp Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 2)) = true Success u = 3.14, ((fpclassify(value) == 2)) = false Success w = inf, ((fpclassify(value) == 2)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == 0x7fffffff)) = true Success u = 3.14, ((ilogb(value) == 0x7fffffff)) = false Success w = inf, ((ilogb(value) == 0x7fffffff)) = true FAILED 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 )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > _ |
综上所述,仅使用该测试程序中定义的EDCOX1 OR 20函数直接测试位级表示,在G+和Visual C++两种情况下都能可靠地工作。
附录:在发布上述内容之后,我意识到还有一种可能测试NaN,在这里的另一个答案中提到,即
1 2 3 4 5 6 7 8 9 10 11 | 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); } |
如果
在运行期间,这只是比较,铸件不需要任何时间。它只是更改比较标志配置以检查相等性。
对于我来说,解决方案可以是一个宏,使其显式内联,从而足够快。它也适用于任何浮动类型。它基于这样一个事实,即当一个值不等于它本身时,唯一的情况是该值不是一个数字。
1 2 3 | #ifndef isnan #define isnan(a) (a != a) #endif |
考虑到这一点(X!=x)并不总是保证NaN(例如,如果使用-ffast数学选项),我一直使用:
1 | #define IS_NAN(x) (((x) < 0) == ((x) >= 0)) |
数字不能同时小于0和大于等于0,因此实际上,只有当数字既不小于也不大于或等于零时,此检查才会通过。基本上没有数字,也就是NaN。
如果您愿意,也可以使用这个:
1 | #define IS_NAN(x) (!((x)<0) && !((x)>=0) |
我不知道这是如何受快速数学的影响,所以你的里程可能会有所不同。
不依赖于所用NAN的特定IEEE表示的可能解决方案如下:
1 2 3 4 5 | template<class T> bool isnan( T f ) { T _nan = (T)0.0/(T)0.0; return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) ); } |
这工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #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; } |
输出:ISNEN
在我看来,最好的真正跨平台方法应该是使用联合和测试double的位模式来检查nan。
我还没有彻底测试过这个解决方案,也许有一种更有效的方式来处理位模式,但是我认为它应该可以工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #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; } |
这将通过检查无穷大和NaN是否在双重限制范围内来检测Visual Studio中的无穷大和NaN:
1 2 3 4 5 6 | //#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; |
IEEE标准规定指数为1时和尾数不是零,数字是NaN。double是1个符号位、11个指数位和52个尾数位。做一点检查。
正如上面的评论,状态A!=A在G++和其他一些编译器中不起作用,但是这个技巧应该起作用。它可能没有那么有效,但它仍然是一种方式:
1 2 3 4 5 6 7 | bool IsNan(float a) { char s[4]; sprintf(s,"%.3f", a); if (s[0]=='n') return true; else return false; } |
基本上,如果变量不是有效的整数/浮点数,那么printf在%d或.f格式上打印"nan"(我不确定其他格式)。因此,此代码检查字符串的第一个字符是否为"n"(如"nan")。