是否存在isnan()函数?
注:我在MinGW(如果这有区别的话)。
我使用isnan()从<math.h>解决了这个问题,这在<cmath>中不存在,我一开始是#包括在内的。
是否存在isnan()函数?
注:我在MinGW(如果这有区别的话)。
我使用isnan()从<math.h>解决了这个问题,这在<cmath>中不存在,我一开始是#包括在内的。
当前回答
IEEE标准说 当指数都是1时 而且 尾数不是零, 该号码是一个NaN。 Double是1个符号位,11个指数位和52个尾数位。 做一点检查。
其他回答
第一个解决方案:如果您使用c++ 11
既然问了这个问题,就有了一些新的发展:重要的是要知道std::isnan()是c++ 11的一部分
剧情简介
在header <cmath>中定义
bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)
确定给定的浮点数参数是否为非数字(NaN)。
参数
参数:浮点值
返回值
如果arg是NaN则为true,否则为false
参考
http://en.cppreference.com/w/cpp/numeric/math/isnan
请注意,如果您使用g++,这与-fast-math不兼容,请参阅下面的其他建议。
其他解决方案:如果你使用非c++ 11兼容的工具
对于C99,在C中,这是作为一个返回int值的宏isnan(C)实现的。x的类型应为float, double或long double。
不同的供应商可能包含或不包含函数isnan()。
检查NaN的可移植方法是使用IEEE 754属性,即NaN不等于自身:即x == x对于x是NaN将为假。
然而,最后一个选项可能不适用于每个编译器和某些设置(特别是优化设置),所以在最后的手段,你总是可以检查位模式…
下面的代码使用NAN(所有指数位集合,至少一个小数位集合)的定义,并假设sizeof(int) = sizeof(float) = 4。你可以在维基百科中查找NAN的详细信息。
bool IsNan(浮点值) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }
一个可能的解决方案,不依赖于特定的IEEE表示NaN使用如下:
template<class T>
bool isnan( T f ) {
T _nan = (T)0.0/(T)0.0;
return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
截至c++ 14,有许多方法来测试浮点数值是否为NaN。
Of these ways, only checking of the bits of the number's representation, works reliably, as noted in my original answer. In particular, std::isnan and the often proposed check v != v, do not work reliably and should not be used, lest your code stops working correctly when someone decides that floating point optimization is needed, and asks the compiler to do that. This situation can change, compilers can get more conforming, but for this issue that hasn't happened in the 6 years since the original answer.
在大约6年的时间里,我最初的答案是这个问题的选择解决方案,这是OK的。但最近,一个高度好评的答案推荐了不可靠的v != v测试。因此,这是一个额外的最新答案(我们现在有c++ 11和c++ 14标准,以及即将出现的c++ 17标准)。
从c++ 14开始,检查nan属性的主要方法是:
std::isnan(value) ) is the intended standard library way since C++11. isnan apparently conflicts with the Posix macro of the same name, but in practice that isn't a problem. The main problem is that when floating point arithmetic optimization is requested, then with at least one main compiler, namely g++, std::isnan returns false for NaN argument. (fpclassify(value) == FP_NAN) ) Suffers from the same problem as std::isnan, i.e., is not reliable. (value != value) ) Recommended in many SO answers. Suffers from the same problem as std::isnan, i.e., is not reliable. (value == Fp_info::quiet_NaN()) ) This is a test that with standard behavior should not detect NaNs, but that with the optimized behavior maybe could detect NaNs (due to optimized code just comparing the bitlevel representations directly), and perhaps combined with another way to cover the standard un-optimized behavior, could reliably detect NaN. Unfortunately it turned out to not work reliably. (ilogb(value) == FP_ILOGBNAN) ) Suffers from the same problem as std::isnan, i.e., is not reliable. isunordered(1.2345, value) ) Suffers from the same problem as std::isnan, i.e., is not reliable. is_ieee754_nan( value ) ) This isn't a standard function. It's checking of the bits according to the IEEE 754 standard. It's completely reliable but the code is somewhat system-dependent.
在下面完整的测试代码中,“success”是表达式是否报告值的Nan-ness。对于大多数表达式来说,这种成功的度量,即检测nan(且仅检测nan)的目标与它们的标准语义相对应。然而,对于(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 ) );
}
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++的- fast-math选项中失败了。只有直接位模式测试才能可靠地工作。
有三种“正式”的方法:posix isnan宏,c++0x isnan函数模板,或visual c++ _isnan函数。
不幸的是,要检测使用哪一种是相当不切实际的。
不幸的是,没有可靠的方法来检测是否有IEEE 754表示的nan。标准库提供了一种正式的方法(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 );
}
使用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> _