Why does MSVS not optimize away +0?
这个问题展示了一个非常有趣的现象:非规范化的浮动会使代码慢下来一个数量级以上。
这个行为在公认的答案中得到了很好的解释。但是,有一条评论,目前有153张赞成票,我无法找到令人满意的答案:
Why isn't the compiler just dropping the +/- 0 in this case?!? –
Michael Dorgan
号
side note:我认为0f是/必须是完全可表示的(而且-它的二进制表示必须全部为零),但在C11标准中找不到这样的声明。一个证明这一点的引言,或反驳这一主张的论点,将是最受欢迎的。无论如何,迈克尔的问题是这里的主要问题。
第5.2.4.2.2条
An implementation may give zero and values that are not floating-point
numbers (such as infinities and NaNs) a sign or may leave them
unsigned.
号
- 在链接问题答案的最后一条注释中回答:"@s73v3r:the+0.f cannot optimized out because floating-point has a negative 0,and the result of adding+0.f to-.0f is+0.f.so adding 0.f is not an identity operation and cannot be optimized out.–埃里克产后
- 很明显,非规范化的不是+0.f或-0.f,而是数组中加零的值,即非规范化(并导致减速)。
- @迈克尔·伯尔,看我的编辑。顺便问一下,你认为问题中的小字体是正确的吗?或者我应该单独问这个问题吗?
- 我认为编辑不会改变任何东西。MSVC使用的浮点的实现使用有符号零。这可能不是C标准所要求的,但可能是IEEE 754所要求的(我真的不知道)。但是,/fp:fast选项可能会导致编译器优化+0.f-我不知道。
- 我不认为C或C++标准指定浮点0应该如何表示。然而,我的理解是,IEEE754规定零由所有零位表示(负零的情况下的符号位除外)。但我远不是浮点数方面的专家,对IEEE标准的细节几乎一无所知。所以,我在评论中所说的可能不是很有用。
- 现在它有153张选票。
- 类似,如果不重复:为什么clang优化了x*1.0而不是x+0.0?
编译器无法消除浮点正零的加法,因为它不是标识操作。根据IEEE754规则,加+0的结果。到-0。不是-0。;是+0。
编译器可以消除+0的减法。或者加上-0。因为这些都是身份操作。
例如,当我编译这个时:
1
| double foo(double x) { return x + 0.; } |
在Intel Mac上使用-O3的Apple GNU C 4.2.1,生成的汇编代码包含addsd LC0(%rip), %xmm0。当我编译这个时:
1
| double foo(double x) { return x - 0.; } |
号
没有添加指令;程序集只返回其输入。
因此,原始问题中的代码可能包含此语句的ADD指令:
但没有对这一声明作出任何指示:
。
但是,第一条语句涉及y[i]中具有次正态值的算术运算,因此足以减慢程序的速度。
非规范化的不是零常数0.0f,而是循环每次迭代接近零的值。随着它们越来越接近于零,它们需要更精确的表示,因此需要非规范化。在最初的问题中,这些是y[i]值。
代码的慢版本和快版本之间的关键区别是语句y[i] = y[i] + 0.1f;。一旦执行此行,浮点中的额外精度将丢失,不再需要表示该精度的非规范化。之后,y[i]上的浮点操作保持快速,因为它们没有非规范化。
为什么添加0.1f时会失去额外的精度?因为浮点数只有这么多有效数字。假设您有足够的存储空间来存储三个有效数字,那么至少对于本例的float格式,0.00001 = 1e-5和0.00001 + 0.1 = 0.1,因为它没有空间来存储0.10001中的最低有效位。
- 我明白了为什么我最近因为没有像现在这样回答这个问题而被投了反对票。最初,这个问题的标题是"为什么MSV不能优化掉+0?"相反,它把它变成一个非规范化的浮动?"我的回答是试图澄清第二部分的困惑。当然,+0.0f和-0.0f之间也存在差异,这就回答了主要问题。