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


368

有一个isnan()函数吗?

PS .:我在MinGW(如果有帮助)。

我该用isnan()的解决<math.h>,这是不存在的<cmath>,这让我#include在第一荷兰国际集团。


2
我不纯粹可以随便做。谁说C ++需要IEEE754?
David Heffernan'3


请注意,预防1盎司优于治疗1磅。换句话说,阻止0.f / 0.f的执行远比追溯检查nan代码中的更好。nan如果对程序有害,它可能会给您的程序造成极大的破坏,它可能导致难以发现的错误。这是因为nan有毒(5 * nan= nannan不等于(nan!= nan),nan不大于(nan!> 0),nan不小于(nan!<0)。
bobobobo

1
@bobobobo:这是一个功能,允许集中式错误检查。就像异常与返回值一样。
Ben Voigt 2015年

2
为什么<cmath>没有isnan()?它在std中::
frankliuao

Answers:


349

根据IEEE标准,NaN值具有奇数性质,即涉及它们的比较始终为假。也就是说,对于浮点数f,当f为NaN 时才f != f为真。

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

对于声称使用IEEE浮点的任何编译器,此技巧都应该起作用。但是我不能保证它在实践中起作用。如有疑问,请与您的编译器联系。


4
如果在IEEE模式下运行,编译器最好不要删除它。当然,请查看您的编译器的文档...
dmckee ---前主持人小猫

38
-1仅在理论上起作用,而在实践中不起作用:g ++(带有-fastmath)之类的编译器将其搞砸了。直到c ++ 0x,唯一通用的方法是测试位模式。
干杯和健康。-Alf

66
@Alf:该-ffast-math选项的文档明确表示,如果数学函数的IEEE或ISO规则/规范依赖于确切的实现,则可能导致程序输出不正确。如果未启用该选项,则使用x != xNaN是一种非常有效且可移植的NaN测试方法。
亚当·罗森菲尔德

7
@Adam:文档确实公开声明它不符合要求,是的。是的,我之前遇到过这种争论,曾与加布里埃尔·多斯·雷斯(Gabriel Dos Reis)进行了详尽的讨论。它通常用于通过循环论证来捍卫设计(我不知道您是否打算与之相关,但值得一提-它是火焰战争的东西)。x != x没有该选项的结论是不合逻辑的。特定版本的g ++可能适用,也可能不适用。无论如何,您通常无法保证不会使用fastmath选项。
干杯和健康。-Alf

7
@Alf:不,我不知道您与Gabriel Dos Reis的讨论。史蒂夫·杰索普(Steve Jessop)在关于假设IEEE表示的另一个问题中提出了重要观点。如果您假定IEEE 754,并且编译器以一致的方式运行(即不带-ffast-math选项),那么这x != x是一种有效且可移植的解决方案。您甚至可以-ffast-math通过测试__FAST_MATH__宏来进行测试,并在这种情况下切换到其他实现(例如,使用联合和位旋转)。
亚当·罗森菲尔德

220

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

在2005年提出了技术报告1。TR1将与C99的兼容性引入C ++。尽管事实上它从未被正式采纳为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 ++(通过cmath或包含math.h),这可能引起更多的混乱,并且开发人员可能会认为这是标准行为。

约Viusal C A注++,如上所述,它不提供std::isnan既不std::tr1::isnan,但它提供了定义为一个扩展函数_isnan()这一直是因为现有的Visual C ++ 6.0

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


21
每个人都想要像isNan或isInfinity这样的功能。为什么负责人不只是将其纳入标准????-我将尝试找出如何负责并为此投票。说真的
shuhalo 2011年

8
@shuhalo负责了吗?
托马什Zato -恢复莫妮卡

11
由于std::isnan现在已成为C ++ 11标准的一部分,并且支持范围已经扩展,因此应该更新此答案。从Visual Studio 2013开始,在Visual Studio中实现了std :: isnan。也许@shuhalo负责了:-)
aberaud

170

第一个解决方案:如果您使用的是C ++ 11

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

概要

在标头中定义 <cmath>

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如果arg是NaNfalse否则

参考

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

请注意,如果您使用g ++,则它与-fast-math不兼容,请参见以下其他建议。


其他解决方案:如果使用不符合C ++ 11的工具

对于C99,在C中,这被实现为isnan(c)返回int值的宏。的类型x应为float,double或long double。

各种供应商可能包含或不包含功能isnan()

要检查理应可移植的方法NaN是使用IEEE 754属性,NaN不等于本身:即x == x将是错误的xNaN

但是,最后一个选项可能不适用于所有编译器和某些设置(尤其是优化设置),因此,在万不得已时,您始终可以检查位模式...


8
绝对值得被接受,也应该得到更多支持。感谢您的提示
LBES

3
std::isnan截至2017年2月,-1仍然不是一个好的建议,因为它不适用于g ++的浮点优化。
干杯和健康。-Alf

@ Cheersandhth.-Alf:此选项是否符合IEEE标准?答案已被编辑
BlueTrin

@BlueTrin:两个x != xisnan需要的IEEE 754合规工作。关于后者,IEEE 754-2008标准声明“实施应为所有支持的算术格式提供以下非计算操作”,并且“当且仅当x是NaN时,isNaN(x)才为真”。为了检查标准要求is754version1985()和的一致性is754version2008(),而C ++提供了std::numeric_limits<Fp>::is_iec559()(IEC 559是同一标准)。不幸的是,通过-ffast-math优化,例如g ++声称符合标准,但不符合标准。
干杯和健康。-Alf

1
警告:isnan(x)在gcc和clang中不适用于选项-ffinite-math-only
A Fog

82

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的整个Math工具箱,它具有许多有用的工具并且正在迅速发展。

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


1
谢谢!正是我想要的。
沃森博士,

它是在Boost 1.35中添加的(我刚刚发现我的程序无法在旧版Linux发行版上编译)。
marcin

2
如果使用选项--fast-math进行编译,则此功能将无法正常工作。
加埃塔诺·门多拉

43

有三种“官方”方式:posix isnan,c ++ 0x isnan函数模板或可视c ++ _isnan函数

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

不幸的是,没有可靠的方法来检测您是否具有带有NaN的IEEE 754表示形式。标准库提供了一种官方的方法(numeric_limits<double>::is_iec559)。但是实际上,诸如g ++之类的编译器会搞砸了。

从理论上讲,可以简单地使用x != x,但是g ++和visual c ++之类的编译器可以解决这一问题。

因此,最后,测试特定的NaN位模式,并假设(并希望在某些时候实施!)特定的表示形式,例如IEEE 754。


编辑:作为“例如g ++的编译器……搞砸了”的示例,请考虑

#include <limits>
#include <assert.h>

void foo( double a, double b )
{
    assert( a != b );
}

int main()
{
    typedef std::numeric_limits<double> Info;
    double const nan1 = Info::quiet_NaN();
    double const nan2 = Info::quiet_NaN();
    foo( nan1, nan2 );
}

使用g ++(TDM-2 mingw32)4.4.1进行编译:

C:\ test>键入“ 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> &&回声有效... || 回声!失败
作品...

C:\ test> gnuc x.cpp --fast-math

C:\ test> &&回声有效... || 回声!失败
断言失败:a!= b,文件x.cpp,第6行

该应用程序已请求运行时以一种异常方式终止它。
请与应用程序的支持团队联系以获取更多信息。
失败

C:\ test> _

4
@Alf:您的示例在Mac OS X和Linux以及4.0和4.5之间的各种版本的g ++上都可以正常工作。该-ffast-math选项的文档明确指出,如果数学函数的IEEE或ISO规则/规范依赖于确切的实现,则可能导致程序输出不正确。如果未启用该选项,则使用x != xNaN是一种非常有效且可移植的NaN测试方法。
亚当·罗森菲尔德

6
@Adam:您缺少的是C ++标准不需要浮点数的IEEE表示或数学。据手册页告诉您的,gcc -ffast-math它仍然是符合标准的C ++实现(嗯,假设它numeric_limits::is_iec559是对的,尽管Alf在上面提出了建议,但事实并非如此):依赖IEEE的C ++代码不是可移植的C ++,并且没有权利期望实现能够提供它。
史蒂夫·杰索普

5
而且Alf在gcc 4.3.4上进行了正确的快速测试,并且使用is_iec559确实如此-ffast-math。因此,这里的问题在于,GCC的文档-ffast-math仅说它是用于数学函数的非IEEE / ISO,而他们应该说它是非C ++,因为它的实现numeric_limits很糟糕。我猜想,GCC不能总是在定义模板时告诉您最终的后端是否实际上具有符合要求的浮点数,因此甚至都不会尝试。IIRC在GCC的C99一致性未解决的错误列表中也存在类似的问题。
史蒂夫·杰索普

1
@ Alf,@ Steve,我不知道C ++标准没有有关浮点值的规范。这真让我震惊。将IEEE 754和NaN作为特定于平台的扩展而不是标准来处理,看起来更好。是不是 我可以期望在C ++ 0x中添加任何形式的isnan()或IEEE754吗?
Eonil'3

3
@Eonil:例如,C ++ 0x仍然具有“浮点类型的值表示形式是由实现定义的”。C和C ++都旨在支持在没有浮点硬件的机器上的实现,并且正确的IEEE 754浮点型的仿真比相当合理的替代方案要慢得多。从理论上讲,您可以断言is_iec559是否需要IEEE,实际上在GCC上似乎不起作用。C ++ 0x确实具有isnan功能,但是由于GCC is_iec559现在无法正确实现,我想它也不会在C ++ 0x中使用,并且很-ffast-math可能破坏它的isnan
史蒂夫·杰索普

39

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

如果您的编译器没有标准功能,这是一个小功能,应该可以使用:

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

6
为什么不只是var!= var?
布赖恩·邦迪

8
这样做时,编译器将有机会优化比较,并始终返回true。
CTT

23
不,没有。这样做的编译器已损坏。您可能还说标准库有可能isnan返回错误的结果。从技术上讲,编译器可能有错误,但实际上,不会发生。与相同var != var。之所以有效,是因为这就是IEEE浮点值的定义方式。
jalf

29
如果设置了-ffast-math,isnan()将无法为gcc返回正确的结果。当然,这种优化被证明破坏了IEEE语义...
Matthew Herrmann

如果设置了-ffast-math,则编译器存在错误。或者更确切地说,如果设置了-ffast-math,那么所有赌注都将关闭,并且您无论如何都不能依赖NaN。
Adrian Ratnapala 2015年

25

您可以使用标准库中numeric_limits<float>::quiet_NaN( )定义的内容limits进行测试。为定义了一个单独的常量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 ++进行了测试。


2
但是要当心-在GCC版本3.2.3中,numerical_limits中似乎有一个错误,因为它对quiet_NaN返回0.0。根据我的经验,GCC的更高版本还可以。
Nathan Kitchen

@内森:很高兴知道。我使用的是4.3.2版,因此我非常了解。
比尔蜥蜴

18

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

#include <cmath>

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

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

我正在使用<cmath>,其中没有isnan!顺便我发现有一个isnan在<math.h>中
哈森

1
如我所说,这是C99的一部分。由于C99不属于任何当前C ++标准的一部分,因此我提供了替代方法。但是由于isnan()可能会包含在即将发布的C ++标准中,所以我在它周围放置了#ifndef指令。
raimue

12

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

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


我相信这在大型字节序平台上也可以使用。字面量0x7fffffff只会简单地放在内存中ff ff ff 7fvalue具有与相同的顺序0x7f800000,因此所有操作都排成一行(没有字节交换)。我想知道是否有人可以在大型endian平台上对此进行测试。
布莱恩·瓦格纳

0x7fff1234也是NaN。所以是0xffffffff
史蒂夫Hollasch

12

南预防

我对这个问题的回答是不要对进行追溯检查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.f,或0.0/0.0nan对您的代码的稳定性是一个可怕的敌人,必须非常仔细地检测并防止它的出现1。其属性nan与普通数字不同:

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

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

在我自己的代码中,我注意到nan值往往会产生难以发现的错误。(请注意,对于或,情况并非如此。(<0)返回,(0 < )返回TRUE,甚至(< )返回TRUE。因此,以我的经验,代码的行为通常是inf-inf-infTRUEinf-infinf仍然是所需的。)

在南下做什么

您想发生的事情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.finf将导致结果(实际上具有如上所述的相当好的/非破坏性的行为)。

请记住,整数除以0会导致运行时异常。因此,您必须始终检查是否将整数除以0。仅仅因为0.0/0.0默默地求值为nan并不意味着您会变得懒惰并且0.0/0.0在发生之前不进行检查。

1 检查过nanx != x有时是不可靠的(x != x被某些优化的编译器所剥夺,这些编译器破坏了IEEE的合规性,尤其-ffast-math是在启用开关时)。


感谢您指出了这一点; 这样的编程绝对可以解决此类问题。但是,下次,请尽量不要过多使用文本格式设置功能。像这样切换字体大小,粗细和样式确实很难阅读。
Magnus

4
请注意,0.0 / 0.0不是唯一可能导致NaN的操作。负数的平方根返回NaN。+ infinity的余弦也返回NaN。x不在[0,pi]范围内的操作acos(x)也会导致NaN。简而言之,还必须格外小心,不仅要注意0.0 / 0.0,还要注意这些潜在的风险操作。
鲍里斯·达尔斯泰因

完全同意鲍里斯。根据我的经验,NaN实际上总是来自于sqrt(-1.302e-53)之类的东西,即接近零的中间计算结果被输入到sqrt中而不检查负性。
hans_meine

1
“预防NaN”意味着您需要深入所有基本的算术运算,而不仅仅是除法运算。您需要注意∞/∞,0 *∞,∞%x,x%0,∞-∞,0 ^ 0,∞^ 0等。使用这种基本的算术运算“预防”意味着您将完全降低性能(并且可能会错过您未想到的其他情况)。
史蒂夫·霍拉斯

11

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

在这些方式中,仅检查数字表示的位数是可靠的,如我最初的回答所述。特别std::isnan是经常提出的检查v != v不能可靠地工作并且不应使用,以免在有人确定需要浮点优化并要求编译器执行此操作时,您的代码停止正确工作。这种情况可能会改变,编译器会变得更加合规,但是对于这个问题,自原始答案以来的6年中还没有发生过。

在大约6年的时间里,我最初的答案是为此问题选择的解决方案,没问题。但是最近选择了一个高度推荐的答案,建议该v != v测试不可靠。因此,这个额外的最新答案(我们现在有了C ++ 11和C ++ 14标准以及即将出现的C ++ 17)。


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

  • std::isnan(value) )
    自C ++ 11起就是标准的库方法。isnan显然与同名的Posix宏发生冲突,但是实际上这不是问题。主要问题是,当请求浮点算术优化时,然后至少使用一个主编译器(即g ++)为NaN参数std::isnan 返回false

  • (fpclassify(value) == FP_NAN) )
    遭受与相同的问题std::isnan,即不可靠。

  • (value != value) )
    在许多SO答案中都推荐使用。遭受与相同的问题std::isnan,即不可靠。

  • (value == Fp_info::quiet_NaN()) )
    这项测试使用标准行为不能检测到NaN,但是使用优化行为可能可以检测到NaN(由于优化代码只是直接比较位级表示),并且可能与覆盖标准未优化行为的另一种方式结合使用,可以可靠地检测到NaN。不幸的是,事实证明它不能可靠地工作。

  • (ilogb(value) == FP_ILOGBNAN) )
    遭受与相同的问题std::isnan,即不可靠。

  • isunordered(1.2345, value) )
    遭受与相同的问题std::isnan,即不可靠。

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


在下面的完整测试代码中,“成功”是一个表达式是否报告该值的数值。对于大多数表达式而言,这种成功度量是检测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(检测NaN)]
> g ++ --version | 找到“ ++”
g ++(x86_64-win32-sjlj-rev1,由MinGW-W64项目构建)6.3.0

[C:\ my \ forums \ so \ 282(检测NaN)]
> g ++ foo.cpp &&一个
编译器声称IEEE 754 = true

v = nan,(std :: isnan(value))= true成功
u = 3.14,(std :: isnan(value))=假成功
w = inf,(std :: isnan(value))=假成功

v = nan,(((fpclassify(value)== 0x0100))= true成功
u = 3.14,([[fpclassify(value)== 0x0100))=否成功
w = inf,([[fpclassify(value)== 0x0100))=错误成功

v = nan,(((value!= value))= true成功
u = 3.14,([[value!= value))=否成功
w = inf,(((value!= value))= false成功

v = nan,(((值== Fp_info :: quiet_NaN()))=假失败
u = 3.14,(((value == Fp_info :: quiet_NaN()))=错误成功
w = inf,(((value == Fp_info :: quiet_NaN()))=错误成功

v = nan,(((ilogb(value)==((int)0x80000000)))= true成功
u = 3.14,(((ilogb(value)==((int)0x80000000)))=错误成功
w = inf,(((ilogb(value)==((int)0x80000000)))=错误成功

v = nan,(isunordered(1.2345,value))= true成功
u = 3.14,(isunordered(1.2345,value))=否成功
w = inf,(isunordered(1.2345,value))= false成功

v = nan,(is_ieee754_nan(value))= true成功
u = 3.14,(is_ieee754_nan(value))= false成功
w = inf,(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(检测NaN)]
> g ++ foo.cpp -ffast-math && a
编译器声称IEEE 754 = true

v = nan,(std :: isnan(value))=假失败
u = 3.14,(std :: isnan(value))=假成功
w = inf,(std :: isnan(value))=假成功

v = nan,([[fpclassify(value)== 0x0100))=错误失败
u = 3.14,([[fpclassify(value)== 0x0100))=否成功
w = inf,([[fpclassify(value)== 0x0100))=错误成功

v = nan,(((值!=值)))=假失败
u = 3.14,([[value!= value))=否成功
w = inf,(((value!= value))= false成功

v = nan,(((value == Fp_info :: quiet_NaN()))= true成功
u = 3.14,(((value == Fp_info :: quiet_NaN()))= true失败
w = inf,(((value == Fp_info :: quiet_NaN()))= true失败

v = nan,(((ilogb(value)==((int)0x80000000)))= true成功
u = 3.14,(((ilogb(value)==((int)0x80000000)))=错误成功
w = inf,(((ilogb(value)==((int)0x80000000)))=错误成功

v = nan,(isunordered(1.2345,value))=假失败
u = 3.14,(isunordered(1.2345,value))=否成功
w = inf,(isunordered(1.2345,value))= false成功

v = nan,(is_ieee754_nan(value))= true成功
u = 3.14,(is_ieee754_nan(value))= false成功
w = inf,(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(检测NaN)]
> _

使用Visual C ++的结果:

[C:\ my \ forums \ so \ 282(检测NaN)]
> cl / nologo- 2>&1 | 找到“ ++”
适用于x86的Microsoft(R)C / C ++优化编译器版本19.00.23725

[C:\ my \ forums \ so \ 282(检测NaN)]
> cl foo.cpp / Feb && b
foo.cpp
编译器声称IEEE 754 = true

v = nan,(std :: isnan(value))= true成功
u = 3.14,(std :: isnan(value))=假成功
w = inf,(std :: isnan(value))=假成功

v = nan,(((fpclassify(value)== 2))=真成功
u = 3.14,([[fpclassify(value)== 2))=错误成功
w = inf,([[fpclassify(value)== 2))=错误成功

v = nan,(((value!= value))= true成功
u = 3.14,([[value!= value))=否成功
w = inf,(((value!= value))= false成功

v = nan,(((值== Fp_info :: quiet_NaN()))=假失败
u = 3.14,(((value == Fp_info :: quiet_NaN()))=错误成功
w = inf,(((value == Fp_info :: quiet_NaN()))=错误成功

v = nan,(((ilogb(value)== 0x7fffffff))=真成功
u = 3.14,(((ilogb(value)== 0x7fffffff)))=错误成功
w = inf,(((ilogb(value)== 0x7fffffff))=真失败

v = nan,(isunordered(1.2345,value))= true成功
u = 3.14,(isunordered(1.2345,value))=否成功
w = inf,(isunordered(1.2345,value))= false成功

v = nan,(is_ieee754_nan(value))= true成功
u = 3.14,(is_ieee754_nan(value))= false成功
w = inf,(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(检测NaN)]
> cl foo.cpp / Feb / fp:fast && b
foo.cpp
编译器声称IEEE 754 = true

v = nan,(std :: isnan(value))= true成功
u = 3.14,(std :: isnan(value))=假成功
w = inf,(std :: isnan(value))=假成功

v = nan,(((fpclassify(value)== 2))=真成功
u = 3.14,([[fpclassify(value)== 2))=错误成功
w = inf,([[fpclassify(value)== 2))=错误成功

v = nan,(((value!= value))= true成功
u = 3.14,([[value!= value))=否成功
w = inf,(((value!= value))= false成功

v = nan,(((值== Fp_info :: quiet_NaN()))=假失败
u = 3.14,(((value == Fp_info :: quiet_NaN()))=错误成功
w = inf,(((value == Fp_info :: quiet_NaN()))=错误成功

v = nan,(((ilogb(value)== 0x7fffffff))=真成功
u = 3.14,(((ilogb(value)== 0x7fffffff)))=错误成功
w = inf,(((ilogb(value)== 0x7fffffff))=真失败

v = nan,(isunordered(1.2345,value))= true成功
u = 3.14,(isunordered(1.2345,value))=否成功
w = inf,(isunordered(1.2345,value))= false成功

v = nan,(is_ieee754_nan(value))= true成功
u = 3.14,(is_ieee754_nan(value))= false成功
w = inf,(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(检测NaN)]
> _

总结以上结果,仅使用is_ieee754_nan此测试程序中定义的功能对位级表示形式进行直接测试,在所有情况下均适用于g ++和Visual C ++。


附录:
发布以上内容后,我意识到可以测试NaN的另一种可能性,在这里的另一个答案中提到了((value < 0) == (value >= 0))。事实证明,它可以在Visual C ++上正常工作,但在g ++的-ffast-math选项下却失败了。只有直接的位模式测试才能可靠地工作。


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

在运行时,这只是比较,不需要花费任何时间。它只是更改比较标志配置以检查是否相等。


另请注意,它仅限于IEEE 754表示形式。
干杯和健康。-Alf

请注意,此强制转换违反了g ++的严格别名规则,并且已知编译器在检测到正式UB时会执行Unmentionable Things™。memcpy要确保使用g ++而不是有效的强制类型转换,需要通过字节数组使用。在我的第二个答案中对此进行编码
干杯和健康。-Alf

4

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

单精度浮点具有超过800万个NaN合法且不同的位表示形式,因此您需要添加更多比较。:)
史蒂夫·霍拉斯

4

考虑到(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对它的影响,因此您的里程可能会有所不同。


这实际上是有缺陷的,同样f != f也有缺陷。我已经看到llvm可以优化几乎相同的代码。优化器可以传播有关第一个比较的信息,并弄清楚如果第一个比较是第二个比较可能永远都不成立。(如果编译器严格遵守IEEE规则f != f则要简单得多)
Markus

不适用于g ++的-ffast-math选项。与Visual C ++一起使用。请参阅(stackoverflow.com/a/42138465/464581)。
干杯和健康。-Alf

3

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

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

这是此问题的最佳答案之一!感谢你的分享。
亨利·门克

2
其他答案表明,使用-ffast-math选项集可能会失败。
Technophile '16

3

这有效:

#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


1

在我看来,最好的真正跨平台方法是使用联合并测试double的位模式以检查NaN。

我还没有彻底测试过该解决方案,也许可以使用一种更有效的方式来处理位模式,但是我认为它应该可以工作。

#include <stdint.h>
#include <stdio.h>

union NaN
{
    uint64_t bits;
    double num;
};

int main()
{
    //Test if a double is NaN
    double d = 0.0 / 0.0;
    union NaN n;
    n.num = d;
    if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
    {
        printf("NaN: %f", d);
    }

    return 0;
}

请注意,“从最近未写入的工会成员中读取是未定义的行为”。因此,这种union在两种类型之间进行类型打点的用法可能无法按预期进行(:sad_panda :)。正确的方法(尽管实际上并不太像所希望的那样可移植)将是完全避免double合并,并从中将memcpy 转换为其他uint64_t变量,然后使用该辅助变量进行测试。
Eljay

0

在x86-64上,您可以使用非常快速的方法来检查NaN和无穷大,无论使用哪种-ffast-math编译器选项,它们都可以工作。(f != fstd::isnanstd::isinf总是产生false-ffast-math)。


通过检查最大指数,可以轻松地测试NaN,无穷大和有限数。无穷大是尾数为零的最大指数,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; }

段的std版本isinf并从段中isfinite加载2个double/float常量.data,在最坏的情况下,它们可能导致2个数据高速缓存未命中。上面的版本不加载任何数据,inf_double_shl1并且inf_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 ,则指令会设置奇偶校验标志。std::isnan当没有-ffast-math指定选项时,这是如何工作的。


-1

IEEE标准规定,当指数均为1s且尾数不为零时,数字为a NaN。Double是1符号位,11指数位和52尾数位。做一点检查。


-3

如上面的注释所述,!= 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”)


2
如果a = 234324.0f,这是否会导致缓冲区溢出?
Mazyod

是的,或者340282346638528859811704183484516925440.000a = FLT_MAX。他必须使用char s[7]; sprintf(s, "%.0g", a);,如果使用则为6 chrs a=-FLT_MAX,否则-3e+38
bobobobo

-3

通过检查其是否在双精度限制内,这可以检测到无限以及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;

检查的定义FLT_MINDBL_MINLDBL_MIN更加仔细。这些被定义为每种类型的最小归一化值。例如,单精度具有超过800万个合法分母值,这些分母值大于零且小于零FLT_MIN(且不是NaN)。
史蒂夫·霍拉斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.