`x!=x` 是测试 NaN 的可移植方法吗?

Is `x!=x` a portable way to test for NaN?

在 C 中,您可以使用 isnan(x) 测试 NaN 是否为双精度数。然而网上有很多地方,例如这个 SO answer 说你可以简单地使用 x!=x 来代替。

x!=x 是否在任何 C 规范中作为保证测试 x 是否为 NaN 的方法?我自己找不到它,我希望我的代码适用于不同的编译器。

NaN 作为唯一值 x 和 属性 x!=x 是 IEEE 754 保证。在 C 中识别 NaN 是否是一个忠实的测试归结为变量的表示和操作映射到您打算使用的编译器中的 IEEE 754 格式和操作的紧密程度。

你应该特别担心“过高的精度”和编译器处理它的方式。当 FPU 仅方便地支持比编译器希望用于 floatdouble 类型的更宽格式的计算时,就会发生精度过高的情况。在这种情况下,可以以更宽的精度进行计算,并在编译器以不可预测的方式感觉它时四舍五入到类型的精度。

C99 标准定义了一种处理这种过高精度的方法,它保留了 属性 只有 NaN 与自身不同的特性,但在 1999 年之后的很长一段时间内(甚至在编译器作者不关心的今天) ),在存在超精度的情况下,如果编译器选择舍入计算的超精度结果,则 x != x 对于包含计算的有限结果的任何变量 x 可能为真在第一个 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 当且仅当 x 为 NaN),或者编译器具有超精度的非标准实现,其中x != x 不是 NaN 的可靠测试。

请参考C标准的规范部分Annex F: IEC 60559 floating-point arithmetic

F.1 Introduction

An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex.

Implementations that do not define __STDC_IEC_559__ are not required to conform to these specifications.

F.9.3 Relational operators

The expression x ≠ x is true if x is a NaN.

The expression x = x is false if X is a Nan.

F.3 Operators and functions

The isnan macro in <math.h> provides the isnan function recommended in the Appendix to IEC 60559.