如果 x != x 给出相同的结果,为什么 isnan(x) 存在?

问题描述

众所周知,对于任何浮点类型变量 x != x iff(当且仅当)xNaN(非数字)。或相反的版本:x == x 仅当 x 不是 NaN。那么如果使用语言的自然能力可以获得相同的结果,那么为什么 WG14 决定定义 isnan(x) (math.h)?动机是什么?更好的代码可读性?

额外问题:isnan(x)x != x 之间是否有任何功能差异?

解决方法

是否可以使用语言的自然能力获得相同的结果?

C 不指定 x == x 当当 x 不是 NaN。不过,许多实现都是这样做的。 C 不需要遵守 IEEE_754isnan(x) 定义明确。

使用 isnan(x) 作为可移植代码。


类型表示中的 C(自 C99 起)具有

具有相同对象表示的两个值(NaN 除外)比较相等,但比较相等的值可能具有不同的对象表示。

...但这并没有指定比较 2 个 NAN 的行为。


__STDC_IEC_559__(类似于遵守 IEEE-754)被定义为 1(C 不需要的东西)时,则

“如果 x 是 NaN,则表达式 x != x 为真。”

“如果 x 是 NaN,则表达式 x == x 为假。”

__STDC_IEC_559__ 未定义为 1 时,请谨慎假设浮点数学边缘的行为,例如 NAN 相等。


[编辑以解决一些评论]

C,在 FP 数学的角落,缺乏 IEEE-754 的细节。正如对 IEEE-754 的引用所证明的那样,C89 允许 NAN,但缺少 isnan(x)。没有“具有相同对象表示的两个值(NaN 除外)比较相等,......”来指导。当时,未指定 NAN 的 x==x。在 C99 中,isnan(x) 被定义为明确的 NAN 测试,而不是破坏或使先前的代码无效。在我看来,x==x 对于 NAN 仍未指定,但通常会导致 falseisnan(x) 还提供了代码清晰度。 C 和 NAN 的大部分内容是 fuzzy:往返有效载荷序列信令编码/识别、NAN 可用性,...


isnan(x)x != x 之间在功能上有什么区别吗?

除了上面讨论的 isnan(x)x != x 定义明确的功能之外,还有一些不为人知的功能:

  • isnan(x)x 计算一次对 x != x 的计算两次。如果 x 是类似于 y++ 的某种表达式,则有所不同。

  • isnan(x)sematic 类型进行操作。当“实现支持评估类型中的 NaN 但不支持语义类型中的 NaN”时,这会有所不同。 x != x 对评估类型进行操作。研究FLT_EVAL_METHOD了解更多详情。

,

如果 x != x(或 x == x)给出相同结果,为什么 isnan(x) 存在?

因为它们并不总是给出相同的结果。

例如,当 compiling with -funsafe-math-optimizations 替换时的 GCC

x - x

0.0

so ( x == x ) 即使 xNaN 也可能为真。

-funsafe-math-optimizations is also enabled if either -fast-math or -Ofast is specified

...

-ffast-math

设置选项 -fno-math-errno,-funsafe-math-optimizations,-ffinite-math-only-fno-rounding-math-fno-signaling-nans-fcx-limited-range-fexcess-precision=fast

此选项导致预处理器宏 __FAST_MATH__ 定义。

此选项不会被除 -O 之外的任何 -Ofast 选项打开,因为 它可能会导致依赖于精确的程序的错误输出 数学函数的 IEEE 或 ISO 规则/规范的实现。 但是,它可能会为不需要的程序生成更快的代码 这些规范的保证。

...

-funsafe-math-optimizations

允许对浮点运算进行优化,即 (a) 假设 参数和结果有效并且 (b) 可能违反 IEEE 或 ANSI 标准。在链接时使用时,可能包含库或启动 更改默认 FPU 控制字或其他类似的文件 优化。

此选项不会被任何 -O 选项打开,因为它可能导致 依赖于精确实现的程序的错误输出 IEEE 或 ISO 数学函数的规则/规范。有可能, 但是,为不需要的程序生成更快的代码 这些规格的保证。启用 -fno-signed-zeros-fno-trapping-math-fassociative-math-freciprocal-math

...

因此,在某些情况下,您可能出于性能原因想要使用浮点优化,但仍需要检查 NaN,而唯一的方法是使用 isnan() 之类的内容进行显式检查.

此外,C standard states in 6.2.6.1p4

具有相同对象表示的两个值(NaN 除外)比较相等

实现所需的功能需要能够以某种方式检查 NaN,而不是比较对象表示(位)。因此,isnan() 功能是实现“x == x is false if x is NaN”的先决条件。

必须有某种类型的功能来检查与 NaN 无关的 x == x,否则它只是一个无法实现的无限递归定义。

如果您需要做的只是检查 NaN 并且不需要在实际进行比较时浪费 CPU 周期,那么在 isnan() 之类的内容中公开该功能可以提高性能。>