是否有isnan()函数?
PS.:我在MinGW(如果这有所不同).
我不得不用isnan()从此解决了
,这是不存在的
,这让我#include
在第一荷兰国际集团.
根据IEEE标准,NaN值具有奇怪的特性,即涉及它们的比较总是错误的.也就是说,对于浮点f,只有当f是NaN时f != f
才会成立.
请注意,正如下面的一些评论所指出的那样,并非所有编译器都在优化代码时尊重这一点.
对于声称使用IEEE浮点的任何编译器,这个技巧应该有效.但我不能保证它会在实践中发挥作用.如果有疑问,请咨询您的编译器.
isnan()
当前的C++标准库中没有可用的功能.它是在C99中引入的,定义为宏而不是函数.由C99定义的标准库的元素不是当前C++标准ISO/IEC 14882:1998的一部分,也不是其更新ISO/IEC 14882:2003.
2005年提出了技术报告1.TR1带来了与C99到C++的兼容性.尽管它从未被正式采用成为C++标准,但许多(GCC 4.0+或Visual C++ 9.0 + C++实现确实提供了TR1功能,所有这些功能或仅部分功能(Visual C++ 9.0不提供C99数学功能) .
如果TR1可用,则cmath
包括C99元素,如isnan()
,isfinite()
等,但它们被定义为函数,而不是宏,通常在std::tr1::
命名空间中,尽管许多实现(即Linux上的GCC 4+或Mac OS X 10.5+上的XCode)注入它们直接到std::
,所以std::isnan
定义明确.
此外,C++的一些实现仍然使C99 isnan()
宏可用于C++(通过cmath
或包含math.h
),可能会导致更多混淆,开发人员可能会认为它是标准行为.
约Viusal C++的说明中,如上所述,它不提供std::isnan
既不std::tr1::isnan
,但它提供了定义为一个扩展函数_isnan()
这一直是因为现有的Visual C++ 6.0
在XCode上,还有更多乐趣.如上所述,GCC 4+定义std::isnan
.对于旧版本的编译器和库形式的XCode,似乎(这里是相关的讨论),没有机会检查自己)__inline_isnand()
在Intel和__isnand()
Power PC上定义了两个函数.
由于这被要求有一些新的发展:重要的是要知道它std::isnan()
是C++ 11的一部分
在标题中定义
bool isnan( float arg ); (since C++11) bool isnan( double arg ); (since C++11) bool isnan( long double arg ); (since C++11)
确定给定的浮点数arg是否不是-a-number(NaN
).
参数
arg
:浮点值
返回值
true
如果arg是NaN
,false
否则
参考
http://en.cppreference.com/w/cpp/numeric/math/isnan
请注意,如果您使用g ++,这与-fast-math不兼容,请参阅下面的其他建议.
对于C99,在C中,这是作为isnan(c)
返回int值的宏实现的.类型x
应为float,double或long double.
各种供应商可能包含也可能不包含或不包含功能isnan()
.
要检查理应可移植的方法NaN
是使用IEEE 754属性,NaN
不等于本身:即x == x
将是错误的x
是NaN
.
但是最后一个选项可能不适用于每个编译器和一些设置(特别是优化设置),所以最后,你可以随时检查位模式......
Boost中还有一个只有头文件的库,它有很好的工具来处理浮点数据类型
#include
您将获得以下功能:
templatebool isfinite(T z); template bool isinf(T t); template bool isnan(T t); template bool isnormal(T t);
如果你有时间看看Boost的整个Math工具包,它有许多有用的工具,而且发展很快.
此外,在处理浮点和非浮点时,查看数字转换可能是个好主意.
有三种"官方"方式:posix isnan
宏,c ++ 0x isnan
函数模板或visual c ++ _isnan
函数.
不幸的是,检测哪些使用它是相当不切实际的.
不幸的是,没有可靠的方法来检测您是否使用带有NaN的IEEE 754表示.标准库提供官方这样的方式(numeric_limits
).但在实践中,像g ++这样的编译器搞砸了.
从理论上讲,人们可以简单地使用x != x
,但是像g ++和visual c ++这样的编译器可以搞砸了.
因此,最后,测试特定的NaN位模式,假设(并希望在某些时候执行!)特定表示,例如IEEE 754.
编辑:作为"g ++编译器的一个例子......搞砸了",考虑一下
#include#include void foo( double a, double b ) { assert( a != b ); } int main() { typedef std::numeric_limits Info; double const nan1 = Info::quiet_NaN(); double const nan2 = Info::quiet_NaN(); foo( nan1, nan2 ); }
用g ++编译(TDM-2 mingw32)4.4.1:
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> _
如果编译器支持c99扩展,则有一个std :: isnan,但我不确定mingw是否支持.
这是一个小函数,如果您的编译器没有标准函数,它应该可以工作:
bool custom_isnan(double var) { volatile double d = var; return d != d; }
您可以使用标准库中numeric_limits
定义的方法limits
进行测试.有一个单独的常量定义double
.
#include#include #include using namespace std; int main( ) { cout << "The quiet NaN for type float is: " << numeric_limits ::quiet_NaN( ) << endl; float f_nan = numeric_limits ::quiet_NaN(); if( isnan(f_nan) ) { cout << "Float was Not a Number: " << f_nan << endl; } return 0; }
我不知道这是否适用于所有平台,因为我只在Linux上使用g ++进行测试.
您可以使用该isnan()
功能,但需要包含C数学库.
#include
由于此功能是C99的一部分,因此无法在任何地方使用.如果您的供应商不提供该功能,您还可以定义自己的变体以实现兼容性.
inline bool isnan(double x) { return x != x; }
我对这个问题的回答是不要使用追溯检查nan
.而是使用预防性检查表单的分部0.0/0.0
.
#includefloat 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
操作的结果0.f/0.f
,或0.0/0.0
. nan
对于代码的稳定性来说,这是一个可怕的克星,必须仔细检测和防止1.其属性nan
与正常数字不同:
nan
有毒,(5*nan
= nan
)
nan
不等于任何东西,甚至不是自己(nan
!= nan
)
nan
不超过任何东西(nan
!> 0)
nan
不小于任何东西(nan
!<0)
列出的最后两个属性是反逻辑的,并且会导致依赖于与nan
数字进行比较的代码的奇怪行为(第三个最后一个属性也很奇怪,但您可能不会x != x ?
在代码中看到(除非您正在检查对于nan(不可靠))).
在我自己的代码中,我注意到nan
值往往会产生很难找到的错误.(请注意,或者不是这种情况.(<0)返回,(0 < )返回TRUE,偶数(< )返回TRUE.因此,根据我的经验,代码的行为通常仍然是所希望的.inf
-inf
-inf
TRUE
inf
-inf
inf
你想要发生的事情0.0/0.0
必须作为一个特例来处理,但你所做的事情必须取决于你希望从代码中得到的数字.
在上面的例子中,(0.f/FLT_MIN
)的结果0
基本上是.您可能想要0.0/0.0
生成HUGE
.所以,
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.f
,inf
会产生(实际上如上所述具有相当好/非破坏性的行为).
请记住,整数除以0会导致运行时异常.所以你必须总是检查0的整数除法.只是因为0.0/0.0
安静地评估nan
并不意味着你可以懒惰并且0.0/0.0
在它发生之前不检查.
1 检查nan
通孔x != x
有时是不可靠的(x != x
被一些优化编译器剥离,违反IEEE合规性,特别-ffast-math
是在启用开关时).
以下代码使用NAN的定义(所有指数位集,至少一个小数位集)并假设sizeof(int)= sizeof(float)= 4.您可以在维基百科中查找NAN以获取详细信息.
bool IsNan( float value )
{
return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000;
}
从C++ 14开始,有许多方法可以测试浮点数value
是否为NaN.
在这些方法中,只检查数字表示的位,可靠地工作,如我原来的答案中所述.特别是,std::isnan
经常提出的检查v != v
,不能可靠地工作,不应该使用,以免当某人决定需要浮点优化时,代码停止正常工作,并要求编译器执行此操作.这种情况可能会改变,编译器可以更加符合,但对于这个问题,自原始答案以来的6年内没有发生过.
大约6年来,我的原始答案是这个问题的选定解决方案,这是好的.但最近选择了推荐不可靠v != v
测试的高度赞成的答案.因此,这个额外的更新的答案(我们现在有C++ 11和C++ 14标准,C++ 17即将出现).
从C++ 14开始,检查NaN-ness的主要方法是:
std::isnan(value) )
是自C++ 11以来的标准库.isnan
显然与同名的Posix宏冲突,但在实践中这不是问题.主要问题是当请求浮点算术优化时,至少有一个主编译器,即g ++,std::isnan
返回false
NaN参数.
(fpclassify(value) == FP_NAN) )
遭受同样的问题std::isnan
,即不可靠.
(value != value) )
在许多SO答案推荐.遭受同样的问题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 ) )
这不是标准功能.它根据IEEE 754标准检查位.它完全可靠,但代码在某种程度上取决于系统.
在下面的完整测试代码中,"成功"是表达式是否报告值的Nan-ness.对于大多数表达式而言,这种成功度量,检测NaNs和仅NaNs的目标,对应于它们的标准语义.(value == Fp_info::quiet_NaN()) )
但是,对于表达式,标准行为是它不能用作NaN检测器.
#include// std::isnan, std::fpclassify #include #include // std::setw #include #include // CHAR_BIT #include #include // 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 ; 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 ( 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 ++的结果(再次注意,它的标准行为(value == Fp_info::quiet_NaN())
是它不能用作NaN检测器,这只是非常实际的兴趣):
[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++的结果:
[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)] > _
总结上述结果,只使用is_ieee754_nan
此测试程序中定义的函数直接测试位级表示,在所有情况下都可靠地使用g ++和Visual C++.
附录:
在发布上述内容后,我意识到还有另一种可能来测试NaN,在另一个答案中提到,即((value < 0) == (value >= 0))
.事实证明,使用Visual C++可以正常工作,但是使用g ++的-ffast-math
选项却失败了.只有直接位模式测试可靠地工作.
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); }
这是有效的,如果sizeof(int)
是4并且sizeof(long long)
是8.
在运行时间只是比较,铸件不需要任何时间.它只是更改比较标志配置以检查相等性.