`x!=x` 是测试 NaN 的可移植方法吗?
Posted
技术标签:
【中文标题】`x!=x` 是测试 NaN 的可移植方法吗?【英文标题】:Is `x!=x` a portable way to test for NaN? 【发布时间】:2016-02-28 17:40:24 【问题描述】:在 C 语言中,您可以使用 isnan(x)
测试是否为双精度值。但是网上很多地方,例如这个SO answer 说你可以简单地使用x!=x
来代替。
在任何 C 规范中,x!=x
是保证测试 x 是否为 NaN 的方法吗?我自己找不到它,我希望我的代码可以与不同的编译器一起使用。
【问题讨论】:
你还在努力寻找优化isnan()
的方法吗?您是否真的对您的应用程序进行了基准测试并发现这是瓶颈?
【参考方案1】:
NaN 作为唯一值 x
和属性 x!=x
是 IEEE 754 保证。在 C 中识别 NaN
是否是一个忠实的测试,归结为变量的表示和操作在您打算使用的编译器中映射到 IEEE 754 格式和操作的紧密程度。
您应该特别担心“超精度”以及编译器处理它的方式。当 FPU 仅方便地支持比编译器希望用于 float
和 double
类型的更广泛格式的计算时,会发生超精度。在这种情况下,可以以更广泛的精度进行计算,并在编译器以不可预知的方式感觉时四舍五入到类型的精度。
C99 标准定义了一种处理这种过度精度的方法,该方法保留了只有 NaN 与自身不同的属性,但在 1999 年之后的很长一段时间内(甚至现在编译器的作者都不关心),存在过度精度如果编译器选择在计算第一个 x
和第二个x
。
report 描述了编译器的黑暗时期,他们没有努力实现 C99(可能是因为当时还不是 1999 年,或者是因为他们不够关心)。
2008 post 描述了 GCC 如何在 2008 年开始实施 C99 标准以实现超精度。在此之前,GCC 可以提供上述报告中描述的所有惊喜。
当然,如果目标平台根本没有实现 IEEE 754,那么 NaN 值甚至可能不存在,或者存在并且具有与 IEEE 754 指定的属性不同的属性。常见的情况是编译器非常忠实地实现了 IEEE 754将 FLT_EVAL_METHOD 设置为 0、1 或 2(所有这些都保证 x != x
iff x
是 NaN),或具有超精度的非标准实现的编译器,其中 x != x
不是对 NaN 的可靠测试.
【讨论】:
我认为你的最后一点是关键。x != x
适用于 IEEE 754 浮点,但 IEEE 754 不是强制性的。甚至NaN
本身的存在也无法保证。
isnan(x)
是否真的准确地告诉您,x
在x != x
不能用于相同目的的那些平台上是否为 NaN?我想象一个编译器生成两个不同的代码来计算x
;一个用于isnan(x)
调用,一个用于您在该条件内使用它的任何内容。
换句话说,是否有一个程序使用以下函数f
导致f
在使用这样的编译器构建然后运行时打印出“0 is NaN”? void f(double x) if (x != x) printf("%f is NaN\n");
【参考方案2】:
请参考C标准的规范部分Annex F: IEC 60559 floating-point arithmetic:
F.1 简介
定义
__STDC_IEC_559__
的实现应符合本附件中的规范。未定义
__STDC_IEC_559__
的实现不需要符合这些规范。
F.9.3 关系运算符
如果
x
是NaN
,则表达式x ≠ x
为真。如果
X
是Nan
,则表达式x = x
为假。
F.3 运算符和函数
<math.h>
中的isnan
宏提供了 IEC 60559 附录中推荐的isnan
函数。
【讨论】:
实现是否真的定义了__STDC_IEC_559__
,还是像C++ 版本一样,编译器不想声称兼容?
@Adam,有效的问题! gcc 5.2.1 20151028 确实定义了它,clang 3.5.2-3 没有。对于 x86_64-pc-linux-gnu 目标,即。
@Adam 根据我的经验,C 编译器可能会通过定义他们在实践中不尊重的符号来声明,例如 clang -std=c99 -mno-sse2
声称 FLT_EVAL_METHOD
是零,但实际上它不是这样计算的.我没想到检查它是否定义了__STDC_IEC_559__
:***.com/questions/17663780/…以上是关于`x!=x` 是测试 NaN 的可移植方法吗?的主要内容,如果未能解决你的问题,请参考以下文章