IEEE 754-2008 浮点运算标准和ISO/IEC 10967 语言无关算术 (LIA) 标准,第 1 部分回答为什么会这样。
IEEE 754 § 6.3 符号位
当输入或结果为 NaN 时,本标准不解释 NaN 的符号。但请注意,对位字符串的操作(复制、取反、abs、copySign)指定 NaN 结果的符号位,有时基于 NaN 操作数的符号位。逻辑谓词totalOrder 还受到NaN 操作数的符号位的影响。对于所有其他运算,该标准不指定 NaN 结果的符号位,即使只有一个输入 NaN,或者 NaN 是由无效运算产生的。
当输入和结果都不为 NaN 时,乘积或商的符号是操作数符号的异或;和或差 x − y 的符号被视为和 x + (−y),最多不同于
加数的符号之一;转换、量化操作、roundTo-Integral 操作和 roundToIntegralExact(参见 5.3.1)结果的符号是第一个或唯一操作数的符号。即使操作数或结果为零或无穷大,这些规则也应适用。
当两个符号相反的操作数之和(或两个符号相同的操作数之差)恰好为零时,除 roundTowardNegative 之外的所有舍入方向属性中该和(或差)的符号应为 +0;在该属性下,精确的零和(或差)的符号应为-0。然而,即使 x 为零,x + x = x − (−x) 仍保留与 x 相同的符号。
加法的例子
默认舍入模式下 (四舍五入到最近的,平局到偶数),我们看到x+0.0
产生x
, 除非当x
is -0.0
:在这种情况下,我们有两个符号相反的操作数的和,其和为零,并且 §6.3 第 3 段规则该加法产生+0.0
.
Since +0.0
is not bitwise与原件相同-0.0
, 然后-0.0
是可能作为输入出现的合法值,编译器有义务放入将潜在负零转换为的代码+0.0
.
总结:在默认舍入模式下,x+0.0
, if x
-
is not
-0.0
, then x
本身就是一个可接受的输出值。
-
is
-0.0
,则输出值must be +0.0
,这与按位不同-0.0
.
乘法的例子
默认舍入模式下,不会出现这样的问题x*1.0
. If x
:
减法的例子
默认舍入模式下,减法x-0.0
也是一个空操作,因为它相当于x + (-0.0)
. If x
is
- is
NaN
,则 §6.3p1 和 §6.2.3 的应用方式与加法和乘法大致相同。
- is
+/- infinity
,那么结果是+/- infinity
具有相同的符号。
- 是一个(次)正规数,
x-0.0 == x
always.
- is
-0.0
,那么根据 §6.3p2 我们有“[...] 和的符号,或视为和 x + (−y) 的差 x − y 的符号,最多与加数的符号之一不同;“。这迫使我们分配-0.0
作为结果(-0.0) + (-0.0)
, 因为-0.0
符号不同于none加数,同时+0.0
符号不同于two的加数,违反了本条。
- is
+0.0
,然后这减少到加法情况(+0.0) + (-0.0)
上面考虑的加法的例子,根据 §6.3p3 规则给出+0.0
.
由于对于所有情况,输入值作为输出都是合法的,因此可以考虑x-0.0
无操作,并且x == x-0.0
同义反复。
改变价值的优化
IEEE 754-2008 标准有以下有趣的引用:
IEEE 754 § 10.4 字面意义和值变化优化
[...]
除其他外,以下值更改转换保留了源代码的字面含义:
- 当 x 不为零且不是信号 NaN 并且结果具有与 x 相同的指数时,应用恒等属性 0 + x。
- 当 x 不是信号 NaN 并且结果具有与 x 相同的指数时,应用恒等属性 1 × x。
- 更改安静 NaN 的有效负载或符号位。
- [...]
由于所有 NaN 和所有无穷大共享相同的指数,并且正确舍入的结果x+0.0
and x*1.0
对于有限的x
具有完全相同的大小x
,它们的指数是相同的。
sNaNs
信号 NaN 是浮点陷阱值;它们是特殊的 NaN 值,用作浮点操作数会导致无效操作异常 (SIGFPE)。如果触发异常的循环被优化掉,软件的行为将不再相同。
但是,作为用户2357112在评论中指出,C11 标准明确未定义发信号 NaN 的行为(sNaN
),因此编译器可以假设它们不会发生,因此它们引发的异常也不会发生。 C++11 标准省略了对 NaN 信号行为的描述,因此也未定义它。
舍入模式
在替代舍入模式中,允许的优化可能会改变。例如,在舍入到负无穷大模式,优化x+0.0 -> x
成为允许的,但是x-0.0 -> x
成为禁止的。
为了防止 GCC 采用默认的舍入模式和行为,实验标志-frounding-math
可以传递给 GCC。
结论
铿锵和GCC,即使在-O3
,仍然符合 IEEE-754 标准。这意味着它必须遵守 IEEE-754 标准的上述规则。x+0.0
is 不相同 to x
对全部x
根据这些规则,但是x*1.0
可以选择如此: 也就是说,当我们
- 遵循建议,以不变的方式传递有效负载
x
当它是 NaN 时。
- 保持 NaN 结果的符号位不变
* 1.0
.
- 遵循商/积期间对符号位进行异或的顺序,当
x
is not a NaN.
启用 IEEE-754 不安全优化(x+0.0) -> x
, 标志-ffast-math
需要传递给 Clang 或 GCC。