问题描述
众所周知,对于任何浮点类型变量 x != x
iff(当且仅当)x
是 NaN
(非数字)。或相反的版本:x == x
仅当 x
不是 NaN
。那么如果使用语言的自然能力可以获得相同的结果,那么为什么 WG14 决定定义 isnan(x)
(math.h
)?动机是什么?更好的代码可读性?
额外问题:isnan(x)
和 x != x
之间是否有任何功能差异?
解决方法
是否可以使用语言的自然能力获得相同的结果?
C 不指定 x == x
当当 x 不是 NaN
。不过,许多实现都是这样做的。 C 不需要遵守 IEEE_754。 isnan(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 仍未指定,但通常会导致 false。 isnan(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 ) 即使 x
是 NaN
也可能为真。
-funsafe-math-optimizations
is also enabled if either -fast-math
or -Ofast
is specified:
...
-ffast-math
设置选项 -fno-math-errn
o,-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()
之类的内容中公开该功能可以提高性能。>