是否存在isnan()函数?
注:我在MinGW(如果这有区别的话)。
我使用isnan()从<math.h>解决了这个问题,这在<cmath>中不存在,我一开始是#包括在内的。
是否存在isnan()函数?
注:我在MinGW(如果这有区别的话)。
我使用isnan()从<math.h>解决了这个问题,这在<cmath>中不存在,我一开始是#包括在内的。
当前回答
考虑到(x != x)对于NaN并不总是保证的(比如如果使用- fast-math选项),我一直在使用:
#define IS_NAN(x) (((x) < 0) == ((x) >= 0))
数字不能同时< 0和>= 0,所以实际上只有当数字既不小于也不大于或等于0时,这个检查才会通过。基本上没有数字,或者NaN。
如果你喜欢,你也可以使用这个:
#define IS_NAN(x) (!((x)<0) && !((x)>=0)
我不确定这是如何受到快速数学的影响,所以你的里程可能会有所不同。
其他回答
您可以使用在limits标准库中定义的numeric_limits<float>::quiet_NaN()进行测试。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;
}
我不知道这是否适用于所有平台,因为我只在Linux上用g++进行了测试。
在我看来,最好的真正跨平台的方法是使用联合,并测试double的位模式来检查nan。
我还没有彻底测试这个解决方案,可能有一种更有效的方法来处理比特模式,但我认为它应该有效。
#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;
}
这可以在Visual Studio中通过检查它是否在双重限制范围内来检测无穷大和NaN:
//#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;
可以使用isnan()函数,但需要包含C数学库。
#include <cmath>
因为这个函数是C99的一部分,所以并不是所有地方都可用。如果您的供应商没有提供该功能,您也可以定义自己的变体以实现兼容性。
inline bool isnan(double x) {
return x != x;
}
截至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选项中失败了。只有直接位模式测试才能可靠地工作。