jal*_*alf 342
根据IEEE标准,NaN值具有奇怪的特性,即涉及它们的比较总是错误的.也就是说,对于浮点f,只有当f是NaN时f != f才会成立.
请注意,正如下面的一些评论所指出的那样,并非所有编译器都在优化代码时尊重这一点.
对于声称使用IEEE浮点的任何编译器,这个技巧应该有效.但我不能保证它会在实践中发挥作用.如果有疑问,请咨询您的编译器.
mlo*_*kot 218
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上定义了两个函数.
Blu*_*rin 156
由于这被要求有一些新的发展:重要的是要知道它std::isnan()是C++ 11的一部分
在标题中定义 <cmath>
bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)
Run Code Online (Sandbox Code Playgroud)
确定给定的浮点数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.
但是最后一个选项可能不适用于每个编译器和一些设置(特别是优化设置),所以最后,你可以随时检查位模式......
Ano*_*ous 82
Boost中还有一个只有头文件的库,它有很好的工具来处理浮点数据类型
#include <boost/math/special_functions/fpclassify.hpp>
Run Code Online (Sandbox Code Playgroud)
您将获得以下功能:
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);
Run Code Online (Sandbox Code Playgroud)
如果你有时间看看Boost的整个Math工具包,它有许多有用的工具,而且发展很快.
此外,在处理浮点和非浮点时,查看数字转换可能是个好主意.
Che*_*Alf 43
有三种"官方"方式:posix isnan宏,c ++ 0x isnan函数模板或visual c ++ _isnan函数.
不幸的是,检测哪些使用它是相当不切实际的.
不幸的是,没有可靠的方法来检测您是否使用带有NaN的IEEE 754表示.标准库提供官方这样的方式(numeric_limits<double>::is_iec559).但在实践中,像g ++这样的编译器搞砸了.
从理论上讲,人们可以简单地使用x != x,但是像g ++和visual c ++这样的编译器可以搞砸了.
因此,最后,测试特定的NaN位模式,假设(并希望在某些时候执行!)特定表示,例如IEEE 754.
编辑:作为"g ++编译器的一个例子......搞砸了",考虑一下
#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 );
}
Run Code Online (Sandbox Code Playgroud)
用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> _
CTT*_*CTT 39
如果编译器支持c99扩展,则有一个std :: isnan,但我不确定mingw是否支持.
这是一个小函数,如果您的编译器没有标准函数,它应该可以工作:
bool custom_isnan(double var)
{
volatile double d = var;
return d != d;
}
Run Code Online (Sandbox Code Playgroud)
Bil*_*ard 25
您可以使用标准库中numeric_limits<float>::quiet_NaN( )定义的方法limits进行测试.有一个单独的常量定义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;
}
Run Code Online (Sandbox Code Playgroud)
我不知道这是否适用于所有平台,因为我只在Linux上使用g ++进行测试.
rai*_*mue 18
您可以使用该isnan()功能,但需要包含C数学库.
#include <cmath>
Run Code Online (Sandbox Code Playgroud)
由于此功能是C99的一部分,因此无法在任何地方使用.如果您的供应商不提供该功能,您还可以定义自己的变体以实现兼容性.
inline bool isnan(double x) {
return x != x;
}
Run Code Online (Sandbox Code Playgroud)
bob*_*obo 12
我对这个问题的回答是不要使用追溯检查nan.而是使用预防性检查表单的分部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.
Run Code Online (Sandbox Code Playgroud)
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-infTRUEinf-infinf
你想要发生的事情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.
Run Code Online (Sandbox Code Playgroud)
所以在上面,如果是x 0.f,inf会产生(实际上如上所述具有相当好/非破坏性的行为).
请记住,整数除以0会导致运行时异常.所以你必须总是检查0的整数除法.只是因为0.0/0.0安静地评估nan并不意味着你可以懒惰并且0.0/0.0在它发生之前不检查.
1 检查nan通孔x != x有时是不可靠的(x != x被一些优化编译器剥离,违反IEEE合规性,特别-ffast-math是在启用开关时).
小智 11
以下代码使用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 返回falseNaN参数.
(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 <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 ) );
}
Run Code Online (Sandbox Code Playgroud)
使用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);
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,如果sizeof(int)是4并且sizeof(long long)是8.
在运行时间只是比较,铸件不需要任何时间.它只是更改比较标志配置以检查相等性.
| 归档时间: |
|
| 查看次数: |
351275 次 |
| 最近记录: |