关于C#:为什么MSVS没有优化+0?

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.


编译器无法消除浮点正零的加法,因为它不是标识操作。根据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指令:

1
y[i] = y[i] + 0;

但没有对这一声明作出任何指示:

1
y[i] = y[i] - 0;

但是,第一条语句涉及y[i]中具有次正态值的算术运算,因此足以减慢程序的速度。


非规范化的不是零常数0.0f,而是循环每次迭代接近零的值。随着它们越来越接近于零,它们需要更精确的表示,因此需要非规范化。在最初的问题中,这些是y[i]值。

代码的慢版本和快版本之间的关键区别是语句y[i] = y[i] + 0.1f;。一旦执行此行,浮点中的额外精度将丢失,不再需要表示该精度的非规范化。之后,y[i]上的浮点操作保持快速,因为它们没有非规范化。

为什么添加0.1f时会失去额外的精度?因为浮点数只有这么多有效数字。假设您有足够的存储空间来存储三个有效数字,那么至少对于本例的float格式,0.00001 = 1e-50.00001 + 0.1 = 0.1,因为它没有空间来存储0.10001中的最低有效位。