检查 C++ 中的 double(或 float)是否为 NaN;

发布于 2024-07-13 21:37:26 字数 257 浏览 8 评论 0原文

有 isnan() 函数吗?

PS.:我在 MinGW (如果这有什么不同的话)。

我通过使用 中的 isnan() 解决了这个问题,它在 中不存在,我是 #include一开始。

Is there an isnan() function?

PS.: I'm in MinGW (if that makes a difference).

I had this solved by using isnan() from <math.h>, which doesn't exist in <cmath>, which I was #includeing at first.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(21

墨小沫ゞ 2024-07-20 21:37:26

根据 IEEE 标准,NaN 值具有奇怪的属性,涉及它们的比较总是错误。 也就是说,对于浮点数 f,f != f 仅当 f 为 NaN 时才为 true。

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

对于任何声称使用 IEEE 浮点的编译器,这个技巧应该有效。 但我不能保证它在实践中发挥作用。 如有疑问,请咨询您的编译器。

According to the IEEE standard, NaN values have the odd property that comparisons involving them are always false. That is, for a float f, f != f will be true only if f is NaN.

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. But I can't guarantee that it will work in practice. Check with your compiler, if in doubt.

下雨或天晴 2024-07-20 21:37:26

方案:如果您使用的是 C++11

自从提出这个问题以来,出现了一些新的进展:重要的是要知道 std::isnan() 是 C++11 的一部分

第一个解决

在标头 中定义

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

确定给定浮点数 arg 是否不是数字 (NaN)。

参数

arg:浮点值

返回值

true if arg is NaN, < code>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 将如果 xNaN,则为 false。

然而,最后一个选项可能不适用于每个编译器和某些设置(特别是优化设置),因此在最后的手段中,您始终可以检查位模式...

First solution: if you are using 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

Synopsis

Defined in header <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).

Parameters

arg: floating point value

Return value

true if arg is NaN, false otherwise

Reference

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.


Other solutions: if you using non C++11 compliant tools

For C99, in C, this is implemented as a macro isnan(c)that returns an int value. The type of x shall be float, double or long double.

Various vendors may or may not include or not a function isnan().

The supposedly portable way to check for NaN is to use the IEEE 754 property that NaN is not equal to itself: i.e. x == x will be false for x being NaN.

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 ...

飘过的浮云 2024-07-20 21:37:26

当前 C++ 标准库中没有可用的 isnan() 函数。 它是在 C99 中引入的,并定义为 不是函数。 C99 定义的标准库元素不是当前 C++ 标准 ISO/IEC 14882:1998 的一部分,也不是其更新版 ISO/IEC 14882:2003 的一部分。

2005年提出技术报告1。 TR1 为 C++ 带来了与 C99 的兼容性。 尽管事实上它从未被正式采用成为 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++(通过 cmathmath.h 包含),这可能会导致更多混乱,开发人员可能会认为这是标准行为。

关于 Viusal C++ 的说明,如上所述,它不提供 std::isnan 也不提供 std::tr1::isnan,但它提供了一个定义为 std::isnan 的扩展函数code>_isnan() 自 Visual C++ 6.0

在 XCode 上,还有更多乐趣。 如前所述,GCC 4+ 定义了 std::isnan。 对于旧版本的编译器和库形式的 XCode,似乎(这里是 相关讨论),还没有机会检查自己)定义了两个函数,Intel上的__inline_isnand()和Power PC上的__isnand()

There is no isnan() function available in current C++ Standard Library. It was introduced in C99 and defined as a macro not a function. 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.

In 2005 Technical Report 1 was proposed. The TR1 brings compatibility with C99 to C++. 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).

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 (i.e. GCC 4+ on Linux or in XCode on Mac OS X 10.5+) inject them directly to std::, so std::isnan is well defined.

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.

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

On XCode, there is even more fun. As mentioned, GCC 4+ defines 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.

对你而言 2024-07-20 21:37:26

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

此外,在处理浮点和非浮点时,查看 数字转换

There is also a header-only library present in Boost that have neat tools to deal with floating point datatypes

#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.

Also when dealing with floating and non-floating points it might be a good idea to look at the Numeric Conversions.

痴意少年 2024-07-20 21:37:26

共有三种“官方”方式:posix isnan、c++0x isnan 函数模板、或 Visual c++ _isnan 函数

不幸的是,检测使用哪一个是相当不切实际的。

不幸的是,没有可靠的方法来检测您是否具有 NaN 的 IEEE 754 表示形式。 标准库提供了官方的这种方式(numeric_limits::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> _

There are three "official" ways: posix isnan macro, c++0x isnan function template, or visual c++ _isnan function.

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. The standard library offers an official such way (numeric_limits<double>::is_iec559). But in practice compilers such as g++ screw that up.

In theory one could use simply x != x, but compilers such as g++ and visual c++ screw that up.

So in the end, test for the specific NaN bitpatterns, assuming (and hopefully enforcing, at some point!) a particular representation such as IEEE 754.


EDIT: as an example of "compilers such as g++ … screw that up", consider

#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:

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> _
碍人泪离人颜 2024-07-20 21:37:26

如果编译器支持 c99 扩展,则有一个 std::isnan ,但我不确定 mingw 是否支持。

这是一个小函数,如果您的编译器没有标准函数,它应该可以工作:

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

There is an std::isnan if you compiler supports c99 extensions, but I'm not sure if mingw does.

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;
}
故人爱我别走 2024-07-20 21:37:26

您可以使用 limits 标准库中定义的 numeric_limits::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++ 进行了测试。

You can use numeric_limits<float>::quiet_NaN( ) defined in the limits standard library to test with. There's a separate constant defined for 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.

憧憬巴黎街头的黎明 2024-07-20 21:37:26

您可以使用 isnan() 函数,但需要包含 C 数学库。

#include <cmath>

由于此功能是 C99 的一部分,因此并非在所有地方都可用。 如果您的供应商不提供该功能,您还可以定义自己的变体以实现兼容性。

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

You can use the isnan() function, but you need to include the C math library.

#include <cmath>

As this function is part of C99, it is not available everywhere. 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;
}
揽月 2024-07-20 21:37:26

从 C++14 开始,有多种方法可以测试浮点数是否为 NaN。

在这些方法中,仅检查数字表示的位
正如我原来的答案中所述,工作可靠。 特别是,std::isnan和经常建议的检查v != v,不能可靠地工作,不应该使用,以免当有人决定时你的代码停止正常工作需要浮点优化,并要求编译器这样做。 这种情况可以改变,编译器可以变得更加一致,但对于这个问题来说,自原始答案以来的 6 年里还没有发生过。

大约六年来,我最初的答案是这个问题的选定解决方案,这是可以的。 但最近选择了一个高度赞成的答案,建议使用不可靠的 v != v 测试。 因此,有了这个额外的更新答案(我们现在有了 C++11 和 C++14 标准,以及即将推出的 C++17)。


从 C++14 开始,检查 NaN 的主要方法是:

  • std::isnan(value) )
    是自 C++11 以来预期的标准库方式。 isnan 显然与
    Posix 宏同名,但在实践中这不是问题。 主要问题是
    当请求浮点算术优化时,至少使用一个主编译器,即 g++,std::isnan 对于 NaN 参数返回 false .

  • (fpclassify(value) == FP_NAN) )
    std::isnan 遇到同样的问题,即不可靠。

  • (值!=值))
    在许多 SO 答案中推荐。 遇到与 std::isnan 相同的问题,即
    不可靠。

  • (值 == Fp_info::quiet_NaN()) )
    这是一个使用标准行为不应检测 NaN 的测试,但使用
    优化的行为也许可以检测 NaN(由于优化的代码只是比较
    直接位级表示),也许与另一种方式结合
    覆盖标准的未优化行为,可以可靠地检测 NaN。 很遗憾
    事实证明它无法可靠地工作。

  • (ilogb(值) == FP_ILOGBNAN) )
    std::isnan 遇到同样的问题,即不可靠。

  • isunordered(1.2345,值))
    std::isnan 遇到同样的问题,即不可靠。

  • is_ieee754_nan(值))
    这不是标准功能。 它根据 IEEE 754 检查位
    标准。 它完全可靠但是代码在某种程度上依赖于系统。


在下面的完整测试代码中,“成功”是指表达式是否报告值的 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++ 的 -ffast-math 选项却失败了。 只有直接的位模式测试才能可靠地工作。

As of C++14 there are a number of ways to test if a floating point number value is a 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.

For about 6 years my original answer was the selected solution for this question, which was OK. But recently a highly upvoted answer recommending the unreliable v != v test has been selected. Hence this additional more up-to-date answer (we now have the C++11 and C++14 standards, and C++17 on the horizon).


The main ways to check for NaN-ness, as of C++14, are:

  • 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.


In the following complete test code “success” is whether an expression reports Nan-ness of the value. For most expressions this measure of success, the goal of detecting NaNs and only NaNs, corresponds to their standard semantics. For the (value == Fp_info::quiet_NaN()) ) expression, however, the standard behavior is that it doesn't work as a NaN-detector.

#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):

[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++:

[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++.


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)). That turned out to work fine with Visual C++ but failed with g++'s -ffast-math option. Only direct bitpattern testing works reliably.

旧伤还要旧人安 2024-07-20 21:37:26

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

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

The following code uses the definition of NAN (all exponent bits set, at least one fractional bit set) and assumes that sizeof(int) = sizeof(float) = 4. You can look up NAN in Wikipedia for the details.

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

从来不烧饼 2024-07-20 21:37:26

nan 预防

我对这个问题的回答是不要对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.

nan 由操作 0.f/0.f0.0/0.0 产生。 nan 是代码稳定性的可怕克星,必须非常小心地检测和预防它1nan 与普通数字不同的属性:

  • nan 有毒,(5*nan=nan)
  • nan 不等于任何东西,甚至不等于它本身 (nan != nan)
  • nan 不大于任何东西 ( nan !> 0)
  • nan 不小于任何值 (nan !< 0)

列出的最后 2 个属性是反逻辑的并且将导致依赖于与 nan 数字比较的代码出现奇怪的行为(最后第三个属性也很奇怪,但您可能永远不会看到 x != x ? 在你的代码中(除非你正在检查 nan (不可靠)))。

在我自己的代码中,我注意到 nan 值往往会产生难以发现的错误。 (请注意 inf-inf 的情况不是。(-inf < 0) 返回TRUE,( 0 inf ) 返回 TRUE,甚至 (-inf <inf) 返回 TRUE因此,根据我的经验,代码的行为通常仍然符合预期)。

在 nan 下做什么

你想在 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.

在上面,如果 x 是 0.f,则会产生 inf (实际上具有如上所述的相当好的/非破坏性行为)。

请记住,整数除以 0导致运行时异常。 因此,您必须始终检查整数是否被 0 除。仅仅因为 0.0/0.0 悄悄地计算为 nan 并不意味着您可以偷懒而不检查 0.0 /0.0 在它发生之前。

1 通过 x != x 检查 nan 有时是不可靠的(x != x 被一些破坏的优化编译器剔除IEEE 合规性,特别是当启用 -ffast-math 开关时)。

nan prevention

My answer to this question is don't use retroactive checks for nan. Use preventive checks for divisions of the form 0.0/0.0 instead.

#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 is a terrible nemesis to the stability of your code that must be detected and prevented very carefully1. The properties of nan that are different from normal numbers:

  • nan is toxic, (5*nan=nan)
  • nan is not equal to anything, not even itself (nan != nan)
  • nan not greater than anything (nan !> 0)
  • nan is not less than anything (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))).

In my own code, I noticed that nan values tend to produce difficult to find bugs. (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).

what to do under 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.

In the example above, the result of (0.f/FLT_MIN) will be 0, basically. You may want 0.0/0.0 to generate HUGE instead. 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).

Remember, integer division by 0 causes a runtime exception. 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.

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

扮仙女 2024-07-20 21:37:26
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,则此方法有效。

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

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.

During run time it is only comparison, castings do not take any time. It just changes comparison flags configuration to check equality.

新一帅帅 2024-07-20 21:37:26

不依赖于所使用的 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) );
}

A possible solution that would not depend on the specific IEEE representation for NaN used would be the following:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
¢好甜 2024-07-20 21:37:26

考虑到 (x != x) 并不总是保证 NaN (例如使用 -ffast-math 选项),我一直在使用:

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

数字不能同时是 < 0 且 >= 0,所以实际上只有当数字既不小于也不大于或等于 0 时,此检查才会通过。 这基本上根本就没有数字,或者 NaN。

如果您愿意,也可以使用它:

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

但我不确定 -ffast-math 如何影响它,所以您的里程可能会有所不同。

Considering that (x != x) is not always guaranteed for NaN (such as if using the -ffast-math option), I've been using:

#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. Which is basically no number at all, or 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.

何以心动 2024-07-20 21:37:26

对我来说,解决方案可能是一个宏,使其显式内联,从而足够快。
它也适用于任何浮动类型。 它基于这样一个事实:值不等于自身的唯一情况是该值不是数字。

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

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
风向决定发型 2024-07-20 21:37:26

这有效:

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

输出:isnan

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

长安忆 2024-07-20 21:37:26

在我看来,最好的真正跨平台方法是使用联合并测试双精度的位模式以检查 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;
}

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.

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;
}
云胡 2024-07-20 21:37:26

在 x86-64 上,您可以使用极其快速的方法来检查 NaN 和无穷大,无论 -ffast-math 编译器选项如何,该方法都可以工作。 (f != fstd::isnanstd::isinf 始终使用 产生 false -ffast-math)。


通过检查最大指数可以轻松测试 NaN、无穷大和有限数。 infinity 是最大指数,尾数为零,NaN 是最大指数,尾数非零。 指数存储在最高符号位之后的下一位中,因此我们只需左移即可去掉符号位并使指数成为最高位,无需掩码(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; }

isinfisfinitestd 版本从 .data< 加载 2 个 double/float 常量/code> 段,在最坏的情况下,它们可能会导致 2 次数据缓存未命中。 上述版本不加载任何数据,inf_double_shl1inf_float_shl1 常量被编码为立即操作数到汇编指令中。


更快的 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;
}

使用 ucomisd<如果任何参数为 NaN,/code>指令将设置奇偶校验标志。 这就是在未指定 -ffast-math 选项时 std::isnan 的工作方式。

On x86-64 you can have extremely fast methods for checking for NaN and infinity, which work regardless of -ffast-math compiler option. (f != f, std::isnan, std::isinf always yield false with -ffast-math).


Testing for NaN, infinity and finite numbers can easily be done by checking for maximum exponent. infinity is maximum exponent with zero mantissa, NaN is maximum exponent and non-zero mantissa. 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:

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. 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.


Faster isnan2 is just 2 assembly instructions:

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. This is how std::isnan works when no -ffast-math options is specified.

你如我软肋 2024-07-20 21:37:26

IEEE 标准说
当指数全为 1 时

尾数不为零,
该数字是 NaN
Double 是 1 符号位、11 指数位和 52 尾数位。
稍微检查一下。

The IEEE standard says
when the exponent is all 1s
and
the mantissa is not zero,
the number is a NaN.
Double is 1 sign bit, 11 exponent bits and 52 mantissa bits.
Do a bit check.

徒留西风 2024-07-20 21:37:26

正如上面的注释所说, a != a 在 g++ 和其他一些编译器中不起作用,但这个技巧应该可以。 它可能没有那么有效,但它仍然是一种方法:

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

基本上,在 g++ 中(虽然我不确定其他的),如果变量不是有效的整数/浮点, printf 会在 %d 或 %.f 格式上打印“nan”。 因此,此代码检查字符串的第一个字符是否为“n”(如“nan”)

As comments above state a != a will not work in g++ and some other compilers, but this trick should. 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. Therefore this code is checking for the first character of string to be 'n' (as in "nan")

孤独陪着我 2024-07-20 21:37:26

这通过检查它是否在双重限制内来检测 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;

This detects infinity and also NaN in Visual Studio by checking it is within double limits:

//#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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文