是否存在isnan()函数?

注:我在MinGW(如果这有区别的话)。

我使用isnan()从<math.h>解决了这个问题,这在<cmath>中不存在,我一开始是#包括在内的。


当前回答

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,则此方法有效。

在运行时,它只是比较,类型转换不需要任何时间。它只是改变比较标志配置来检查是否相等。

其他回答

Boost中还提供了一个仅头文件的库,该库具有处理浮点数据类型的简洁工具

#include <boost/math/special_functions/fpclassify.hpp>

你会得到以下函数:

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的整个数学工具包,它有许多有用的工具,并且正在快速增长。

此外,当处理浮点和非浮点时,查看数字转换可能是一个好主意。

可以使用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选项中失败了。只有直接位模式测试才能可靠地工作。

下面的代码使用NAN(所有指数位集合,至少一个小数位集合)的定义,并假设sizeof(int) = sizeof(float) = 4。你可以在维基百科中查找NAN的详细信息。

bool IsNan(浮点值) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }

根据IEEE标准,NaN值有一个奇怪的性质,涉及它们的比较总是假的。也就是说,对于浮点数f, f != f只有在f为NaN时才为真。

请注意,正如下面的一些评论所指出的,并不是所有编译器在优化代码时都尊重这一点。

对于任何声称使用IEEE浮点数的编译器,这个技巧都应该有效。但我不能保证它在实践中会起作用。如果有疑问,请检查编译器。