简体   繁体   English

在 C++ 中检查双精度(或浮点数)是否为 NaN

[英]Checking if a double (or float) is NaN in C++

Is there an isnan() function?有 isnan() 函数吗?

PS.: I'm in MinGW (if that makes a difference). PS.:我在MinGW (如果这有什么不同的话)。

I had this solved by using isnan() from <math.h> , which doesn't exist in <cmath> , which I was #include ing at first.我通过使用<math.h>中的 isnan() 解决了这个问题,它在<cmath>中不存在,我一开始是#include

According to the IEEE standard, NaN values have the odd property that comparisons involving them are always false.根据 IEEE 标准,NaN 值具有奇数特性,即涉及它们的比较总是错误的。 That is, for a float f, f != f will be true only if f is NaN.也就是说,对于浮点数 f,只有当 f 为 NaN 时, f != f才会为真。

Note that, as some comments below have pointed out, not all compilers respect this when optimizing code.请注意,正如下面的一些评论所指出的,并非所有编译器在优化代码时都尊重这一点。

For any compiler which claims to use IEEE floating point, this trick should work.对于任何声称使用 IEEE 浮点的编译器,这个技巧应该可以工作。 But I can't guarantee that it will work in practice.但我不能保证它在实践中发挥作用。 Check with your compiler, if in doubt.如果有疑问,请检查您的编译器。

There is no isnan() function available in current C++ Standard Library.当前 C++ 标准库中没有可用的isnan()函数。 It was introduced in C99 and defined as a macro not a function.它是在C99中引入的,并被定义为而不是函数。 Elements of standard library defined by C99 are not part of current C++ standard ISO/IEC 14882:1998 neither its update ISO/IEC 14882:2003. C99 定义的标准库元素不是当前 C++ 标准 ISO/IEC 14882:1998 的一部分,也不是其更新 ISO/IEC 14882:2003 的一部分。

In 2005 Technical Report 1 was proposed. 2005 年提出了技术报告 1。 The TR1 brings compatibility with C99 to C++. TR1 为 C++ 带来了与 C99 的兼容性。 In spite of the fact it has never been officially adopted to become C++ standard, many ( GCC 4.0+ or Visual C++ 9.0+ C++ implementations do provide TR1 features, all of them or only some (Visual C++ 9.0 does not provide C99 math functions).尽管它从未被正式采用成为 C++ 标准,但许多( GCC 4.0+Visual C++ 9.0+ C++ 实现确实提供了 TR1 特性,全部或仅提供一些特性(Visual C++ 9.0 不提供 C99 数学函数) .

If TR1 is available, then cmath includes C99 elements like isnan() , isfinite() , etc. but they are defined as functions, not macros, usually in std::tr1:: namespace, though many implementations (ie GCC 4+ on Linux or in XCode on Mac OS X 10.5+) inject them directly to std:: , so std::isnan is well defined.如果 TR1 可用,则cmath包括 C99 元素,如isnan()isfinite()等,但它们被定义为函数,而不是宏,通常在std::tr1::命名空间中,尽管有许多实现(即 GCC 4+ on Linux 或 Mac OS X 10.5+ 上的 XCode 中)将它们直接注入std:: ,因此std::isnan定义明确。

Moreover, some implementations of C++ still make C99 isnan() macro available for C++ (included through cmath or math.h ), what may cause more confusions and developers may assume it's a standard behaviour.此外,C++ 的某些实现仍然使 C99 isnan()宏可用于 C++(通过cmathmath.h包含),这可能会导致更多的混淆,开发人员可能会认为这是标准行为。

A note about Viusal C++, as mentioned above, it does not provide std::isnan neither std::tr1::isnan , but it provides an extension function defined as _isnan() which has been available since Visual C++ 6.0关于 Viusal C++ 的注释,如上所述,它既不提供std::isnan也不提供 std std::tr1::isnan ,但它提供了定义为_isnan()的扩展函数,该函数自Visual C++ 6.0起就可用

On XCode, there is even more fun.在 XCode 上,还有更多的乐趣。 As mentioned, GCC 4+ defines std::isnan .如前所述,GCC 4+ 定义了std::isnan For older versions of compiler and library form XCode, it seems (here is relevant discussion ), haven't had chance to check myself) two functions are defined, __inline_isnand() on Intel and __isnand() on Power PC.对于旧版本的编译器和库形式的 XCode,似乎(这里是相关讨论),没有机会检查自己)定义了两个函数,英特尔上的__inline_isnand()和 Power PC 上的__isnand()

First solution: if you are using C++11第一个解决方案:如果您使用的是 C++11

Since this was asked there were a bit of new developments: it is important to know that std::isnan() is part of C++11既然有人问了这个问题,就有了一些新的发展:重要的是要知道std::isnan()是 C++11 的一部分

Synopsis概要

Defined in header <cmath>在标题<cmath>中定义

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

Determines if the given floating point number arg is not-a-number ( NaN ).确定给定的浮点数 arg 是否不是数字 ( NaN )。

Parameters参数

arg : floating point value arg : 浮点值

Return value返回值

true if arg is NaN , false otherwise如果 arg 是NaN则为true ,否则为false

Reference参考

http://en.cppreference.com/w/cpp/numeric/math/isnan http://en.cppreference.com/w/cpp/numeric/math/isnan

Please note that this is incompatible with -fast-math if you use g++, see below for other suggestions.请注意,如果您使用 g++,这与 -fast-math 不兼容,请参阅下面的其他建议。


Other solutions: if you using non C++11 compliant tools其他解决方案:如果您使用不符合 C++11 的工具

For C99, in C, this is implemented as a macro isnan(c) that returns an int value.对于 C99,在 C 中,这被实现为返回 int 值的宏isnan(c) The type of x shall be float, double or long double. x的类型应为 float、double 或 long double。

Various vendors may or may not include or not a function isnan() .各种供应商可能包含或不包含函数isnan()

The supposedly portable way to check for NaN is to use the IEEE 754 property that NaN is not equal to itself: ie x == x will be false for x being NaN .检查NaN的所谓可移植方法是使用NaN不等于自身的 IEEE 754 属性:即x == x对于xNaN将是错误的。

However the last option may not work with every compiler and some settings (particularly optimisation settings), so in last resort, you can always check the bit pattern ...但是,最后一个选项可能不适用于每个编译器和某些设置(特别是优化设置),因此在最后的手段中,您始终可以检查位模式......

There is also a header-only library present in Boost that have neat tools to deal with floating point datatypes Boost 中还有一个仅包含标头的库,它具有处理浮点数据类型的简洁工具

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

You get the following functions:您将获得以下功能:

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);

If you have time then have a look at whole Math toolkit from Boost, it has many useful tools and is growing quickly.如果您有时间,请查看 Boost 的整个数学工具包,它有许多有用的工具并且正在快速增长。

Also when dealing with floating and non-floating points it might be a good idea to look at the Numeric Conversions .此外,在处理浮点数和非浮点数时,查看Numeric Conversions可能是个好主意。

There are three "official" ways: posix isnan macro , c++0x isnan function template , or visual c++ _isnan function .有三种“官方”方式:posix isnan、c++0x isnan函数模板、或visual c++ _isnan函数

Unfortunately it's rather impractical to detect which of those to use.不幸的是,检测使用哪些是相当不切实际的。

And unfortunately, there's no reliable way to detect whether you have IEEE 754 representation with NaNs.不幸的是,没有可靠的方法来检测您是否具有带有 NaN 的 IEEE 754 表示。 The standard library offers an official such way ( numeric_limits<double>::is_iec559 ).标准库提供了官方的这种方式( numeric_limits<double>::is_iec559 )。 But in practice compilers such as g++ screw that up.但在实践中,诸如 g++ 之类的编译器搞砸了。

In theory one could use simply x != x , but compilers such as g++ and visual c++ screw that up.理论上,人们可以简单地使用x != x ,但是诸如 g++ 和 visual c++ 之类的编译器会把它搞砸。

So in the end, test for the specific NaN bitpatterns , assuming (and hopefully enforcing, at some point!) a particular representation such as IEEE 754.所以最后,测试特定的NaN 位模式,假设(并希望在某个时候强制执行!)特定的表示,例如 IEEE 754。


EDIT : as an example of "compilers such as g++ … screw that up", consider编辑:作为“诸如 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 );
}

Compiling with g++ (TDM-2 mingw32) 4.4.1:使用 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> _

There is an std::isnan if you compiler supports c99 extensions, but I'm not sure if mingw does.如果您的编译器支持 c99 扩展,则有一个 std::isnan,但我不确定 mingw 是否支持。

Here is a small function which should work if your compiler doesn't have the standard function:如果您的编译器没有标准功能,这是一个应该可以工作的小功能:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}

You can use numeric_limits<float>::quiet_NaN( ) defined in the limits standard library to test with.您可以使用limits标准库中定义的numeric_limits<float>::quiet_NaN( )进行测试。 There's a separate constant defined for double .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;
}

I don't know if this works on all platforms, as I only tested with g++ on Linux.我不知道这是否适用于所有平台,因为我只在 Linux 上使用 g++ 进行了测试。

You can use the isnan() function, but you need to include the C math library.您可以使用isnan()函数,但需要包含 C 数学库。

#include <cmath>

As this function is part of C99, it is not available everywhere.由于此功能是 C99 的一部分,因此并非随处可用。 If your vendor does not supply the function, you can also define your own variant for compatibility.如果您的供应商不提供该功能,您也可以定义自己的变体以实现兼容性。

inline bool isnan(double x) {
    return x != x;
}

As of C++14 there are a number of ways to test if a floating point number value is a NaN.从 C++14 开始,有多种方法可以测试浮点数value是否为 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.特别是std::isnan和经常建议的检查v != v不能可靠地工作并且不应该使用,以免当有人决定需要浮点优化时您的代码停止正常工作,并要求编译器这样做. 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 年没有发生过。

For about 6 years my original answer was the selected solution for this question, which was OK.大约 6 年来,我最初的答案是这个问题的选定解决方案,这没问题。 But recently a highly upvoted answer recommending the unreliable v != v test has been selected.但最近一个高度赞成的答案推荐不可靠的v != v测试已被选中。 Hence this additional more up-to-date answer (we now have the C++11 and C++14 standards, and C++17 on the horizon).因此,这个额外的更新的答案(我们现在有 C++11 和 C++14 标准,以及即将到来的 C++17)。


The main ways to check for NaN-ness, as of C++14, are:从 C++14 开始,检查 NaN 的主要方法是:

  • std::isnan(value) )
    is the intended standard library way since C++11.是自 C++11 以来预期的标准库方式。 isnan apparently conflicts with the Posix macro of the same name, but in practice that isn't a problem. isnan显然与同名的 Posix 宏冲突,但实际上这不是问题。 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 .主要问题是,当请求浮点算术优化时,使用至少一个主编译器,即 g++, std::isnan为 NaN 参数返回false

  • (fpclassify(value) == FP_NAN) )
    Suffers from the same problem as std::isnan , ie, is not reliable.遭受与std::isnan相同的问题,即不可靠。

  • (value != value) )
    Recommended in many SO answers.在许多 SO 答案中推荐。 Suffers from the same problem as std::isnan , ie, is not reliable.遭受与std::isnan相同的问题,即不可靠。

  • (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.这是一个测试,标准行为不应该检测到 NaN,但优化行为可能可以检测到 NaN(由于优化的代码只是直接比较位级表示),并且可能结合另一种方法来覆盖标准的未优化行为,可以可靠地检测到 NaN。 Unfortunately it turned out to not work reliably.不幸的是,结果证明它不能可靠地工作。

  • (ilogb(value) == FP_ILOGBNAN) )
    Suffers from the same problem as std::isnan , ie, is not reliable.遭受与std::isnan相同的问题,即不可靠。

  • isunordered(1.2345, value) )
    Suffers from the same problem as std::isnan , ie, is not reliable.遭受与std::isnan相同的问题,即不可靠。

  • is_ieee754_nan( value ) )
    This isn't a standard function.这不是标准功能。 It's checking of the bits according to the IEEE 754 standard.它根据 IEEE 754 标准检查位。 It's completely reliable but the code is somewhat system-dependent.它是完全可靠的,但代码有点依赖于系统。


In the following complete test code “success” is whether an expression reports Nan-ness of the value.在下面的完整测试代码中,“成功”是表达式是否报告值的 Nan-ness。 For most expressions this measure of success, the goal of detecting NaNs and only NaNs, corresponds to their standard semantics.对于大多数表达式,这种衡量成功的方法、检测 NaN 和仅检测 NaN 的目标对应于它们的标准语义。 For the (value == Fp_info::quiet_NaN()) ) expression, however, the standard behavior is that it doesn't work as a NaN-detector.但是,对于(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 ) );
}

Results with g++ (note again that the standard behavior of (value == Fp_info::quiet_NaN()) is that it doesn't work as a NaN-detector, it's just very much of practical interest here):使用 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)]
> _

Results with Visual C++: 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)]
> _

Summing up the above results, only direct testing of the bit-level representation, using the is_ieee754_nan function defined in this test program, worked reliably in all cases with both g++ and Visual C++.总结上述结果,仅使用此测试程序中定义的is_ieee754_nan函数直接测试位级表示,在 g++ 和 Visual C++ 的所有情况下都可靠地工作。


Addendum:附录:
After posting the above I became aware of yet another possible to test for NaN, mentioned in another answer here, namely ((value < 0) == (value >= 0)) .在发布上述内容后,我意识到另一种可能测试 NaN,在此处的另一个答案中提到,即((value < 0) == (value >= 0)) That turned out to work fine with Visual C++ but failed with g++'s -ffast-math option.结果证明它在 Visual C++ 上运行良好,但在 g++ 的-ffast-math选项下失败了。 Only direct bitpattern testing works reliably.只有直接位模式测试才能可靠地工作。

nan prevention楠预防

My answer to this question is don't use retroactive checks for nan .我对这个问题的回答是不要对nan使用追溯检查 Use preventive checks for divisions of the form 0.0/0.0 instead.改为对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.

nan results from the operation 0.f/0.f , or 0.0/0.0 . nan来自操作0.f/0.f0.0/0.0的结果。 nan is a terrible nemesis to the stability of your code that must be detected and prevented very carefully 1 . nan是代码稳定性的可怕克星,必须非常小心地检测和防止1 The properties of nan that are different from normal numbers: nan与正常数不同的性质:

  • nan is toxic, (5* nan = nan ) nan有毒,(5* nan = nan )
  • nan is not equal to anything, not even itself ( nan != nan ) nan不等于任何东西,甚至不等于它自己 ( nan != nan )
  • nan not greater than anything ( nan !> 0) nan不大于任何东西 ( nan !> 0)
  • nan is not less than anything ( nan !< 0) nan不亚于任何东西 ( nan !< 0)

The last 2 properties listed are counter-logical and will result in odd behavior of code that relies on comparisons with a nan number (the 3rd last property is odd too but you're probably not ever going to see x != x ? in your code (unless you are checking for nan (unreliably))).列出的最后 2 个属性是反逻辑的,并且会导致依赖于与nan数字比较的代码的奇怪行为(倒数第三个属性也是奇数,但您可能永远不会看到x != x ?在您的代码(除非您正在检查 nan(不可靠)))。

In my own code, I noticed that nan values tend to produce difficult to find bugs.在我自己的代码中,我注意到nan值往往会产生难以发现的错误。 (Note how this is not the case for inf or -inf . ( -inf < 0) returns TRUE , ( 0 < inf ) returns TRUE, and even ( -inf < inf ) returns TRUE. So, in my experience, the behavior of the code is often still as desired). (注意inf-inf的情况并非如此。( -inf < 0)返回TRUE ,( 0 < inf )返回 TRUE ,甚至( -inf < inf )返回 TRUE。所以,根据我的经验,行为的代码通常仍然是所需的)。

what to do under nan在nan下做什么

What you want to happen under 0.0/0.0 must be handled as a special case , but what you do must depend on the numbers you expect to come out of the code.您希望在0.0/0.0下发生的事情必须作为特殊情况处理,但您所做的事情必须取决于您希望从代码中得出的数字。

In the example above, the result of ( 0.f/FLT_MIN ) will be 0 , basically.在上面的例子中,( 0.f/FLT_MIN ) 的结果基本上是0 You may want 0.0/0.0 to generate HUGE instead.您可能希望0.0/0.0改为生成HUGE So,所以,

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.

So in the above, if x were 0.f , inf would result (which has pretty good/nondestructive behavior as mentioned above actually).因此,在上面,如果 x 是0.f ,则会导致inf (实际上如上所述,它具有非常好的/非破坏性行为)。

Remember, integer division by 0 causes a runtime exception .请记住, 整数除以 0 会导致运行时异常 So you must always check for integer division by 0. Just because 0.0/0.0 quietly evaluates to nan doesn't mean you can be lazy and not check for 0.0/0.0 before it happens.因此,您必须始终检查整数除以 0。仅仅因为0.0/0.0悄悄地评估为nan并不意味着您可以偷懒并且在0.0/0.0发生之前不检查它。

1 Checks for nan via x != x are sometimes unreliable ( x != x being stripped out by some optimizing compilers that break IEEE compliance, specifically when the -ffast-math switch is enabled). 1通过x != x检查nan有时是不可靠的( x != x被一些破坏 IEEE 合规性的优化编译器剥离,特别是在启用-ffast-math开关时)。

以下代码使用 NAN 的定义(所有指数位设置,至少一个小数位设置)并假设 sizeof(int) = sizeof(float) = 4。您可以在 Wikipedia 中查找 NAN 以获取详细信息。

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

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);
}

This works if sizeof(int) is 4 and sizeof(long long) is 8.如果sizeof(int)为 4 且sizeof(long long)为 8,则此方法有效。

During run time it is only comparison, castings do not take any time.在运行期间,它只是比较,铸件不需要任何时间。 It just changes comparison flags configuration to check equality.它只是更改比较标志配置以检查相等性。

A possible solution that would not depend on the specific IEEE representation for NaN used would be the following:不依赖于所用 NaN 的特定 IEEE 表示的可能解决方案如下:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}

Considering that (x != x) is not always guaranteed for NaN (such as if using the -ffast-math option), I've been using:考虑到 NaN 并不总是保证 (x != x) (例如,如果使用 -ffast-math 选项),我一直在使用:

#define IS_NAN(x) (((x) < 0) == ((x) >= 0))

Numbers can't be both < 0 and >= 0, so really this check only passes if the number is neither less than, nor greater than or equal to zero.数字不能同时为 < 0 和 >= 0,所以实际上只有当数字既不小于、也不大于或等于零时,此检查才会通过。 Which is basically no number at all, or NaN.这基本上根本就不是数字,或者是 NaN。

You could also use this if you prefer:如果您愿意,也可以使用它:

#define IS_NAN(x) (!((x)<0) && !((x)>=0)

I'm not sure how this is affected by -ffast-math though, so your mileage may vary.我不确定这如何受到 -ffast-math 的影响,因此您的里程可能会有所不同。

As for me the solution could be a macro to make it explicitly inline and thus fast enough.至于我,解决方案可能是一个宏,使其显式内联,因此速度足够快。 It also works for any float type.它也适用于任何浮点类型。 It bases on the fact that the only case when a value is not equals itself is when the value is not a number.它基于这样一个事实,即值不等于自身的唯一情况是值不是数字时。

#ifndef isnan
  #define isnan(a) (a != a)
#endif

This works:这有效:

#include <iostream>
#include <math.h>
using namespace std;

int main ()
{
  char ch='a';
  double val = nan(&ch);
  if(isnan(val))
     cout << "isnan" << endl;

  return 0;
}

output: isnan输出:伊斯南

It seems to me that the best truly cross-platform approach would be to use a union and to test the bit pattern of the double to check for NaNs.在我看来,最好的真正跨平台方法是使用联合并测试双精度的位模式以检查 NaN。

I have not thoroughly tested this solution, and there may be a more efficient way of working with the bit patterns, but I think that it should work.我还没有彻底测试过这个解决方案,并且可能有一种更有效的方式来处理位模式,但我认为它应该可以工作。

#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;
}

On x86-64 you can have extremely fast methods for checking for NaN and infinity, which work regardless of -ffast-math compiler option.在 x86-64 上,您可以使用非常快速的方法来检查 NaN 和无穷大,无论-ffast-math编译器选项如何,这些方法都有效。 ( f != f , std::isnan , std::isinf always yield false with -ffast-math ). f != fstd::isnanstd::isinf总是用-ffast-math产生false )。


Testing for NaN, infinity and finite numbers can easily be done by checking for maximum exponent.通过检查最大指数可以很容易地测试 NaN、无穷大和有限数。 infinity is maximum exponent with zero mantissa, NaN is maximum exponent and non-zero mantissa.无穷大是尾数为零的最大指数,NaN 是最大指数和非零尾数。 The exponent is stored in the next bits after the topmost sign bit, so that we can just left shift to get rid of the sign bit and make the exponent the topmost bits, no masking ( operator& ) is necessary:指数存储在最高符号位之后的下一位中,因此我们只需左移即可摆脱符号位并使指数成为最高位,不需要掩码( operator& ):

static inline uint64_t load_ieee754_rep(double a) {
    uint64_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
    return r;
}

static inline uint32_t load_ieee754_rep(float a) {
    uint32_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
    return r;
}

constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);

// The shift left removes the sign bit. The exponent moves into the topmost bits,
// so that plain unsigned comparison is enough.
static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a)  { return load_ieee754_rep(a) << 1  < inf_float_shl1; }

The std versions of isinf and isfinite load 2 double/float constants from .data segment and in the worst case scenario they can cause 2 data cache misses. isinfisfinitestd版本从.data段加载 2 个double/float常量,在最坏的情况下,它们可能导致 2 个数据缓存未命中。 The above versions do not load any data, inf_double_shl1 and inf_float_shl1 constants get encoded as immediate operands into the assembly instructions.上述版本不加载任何数据, inf_double_shl1inf_float_shl1常量被编码为立即操作数到汇编指令中。


Faster isnan2 is just 2 assembly instructions:更快的isnan2只是 2 条汇编指令:

bool isnan2(double a) {
    bool r;
    asm(".intel_syntax noprefix"
        "\n\t ucomisd %1, %1"
        "\n\t setp %b0"
        "\n\t .att_syntax prefix"
        : "=g" (r)
        : "x" (a)
        : "cc"
        );
    return r;
}

Uses the fact that ucomisd instruction sets parity flag if any argument is NaN.如果任何参数为 NaN,则使用ucomisd指令设置奇偶校验标志这一事实。 This is how std::isnan works when no -ffast-math options is specified.这就是未指定-ffast-math选项时std::isnan的工作方式。

The IEEE standard says when the exponent is all 1 s and the mantissa is not zero, the number is a NaN . IEEE 标准规定,当指数全为1且尾数不为零时,数字为NaN Double is 1 sign bit, 11 exponent bits and 52 mantissa bits. Double 是1符号位、 11指数位和52尾数位。 Do a bit check.做一点检查。

As comments above state a != a will not work in g++ and some other compilers, but this trick should.正如上面的评论所说 a != a 在 g++ 和其他一些编译器中不起作用,但是这个技巧应该。 It may not be as efficient, but it's still a way:它可能效率不高,但它仍然是一种方式:

bool IsNan(float a)
{
    char s[4];
    sprintf(s, "%.3f", a);
    if (s[0]=='n') return true;
    else return false;
}

Basically, in g++ (I am not sure about others though) printf prints 'nan' on %d or %.f formats if variable is not a valid integer/float.基本上,在 g++ 中(我不确定其他人)如果变量不是有效的整数/浮点数,printf 会在 %d 或 %.f 格式上打印“nan”。 Therefore this code is checking for the first character of string to be 'n' (as in "nan")因此,此代码检查字符串的第一个字符是否为“n”(如“nan”)

This detects infinity and also NaN in Visual Studio by checking it is within double limits:这通过检查它是否在双重限制内来检测 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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM