为什么MSVS不能优化+0?
这个问题展示了一个非常有趣的现象: 非规范化浮点数使代码减慢了一个数量级以上。
在接受的答案中很好地解释了这种行为。 但是,有一条评论,目前有48条评论,我找不到满意的答案:
在这种情况下,为什么编译器不会丢弃+/- 0? – 迈克尔多根
旁注:我的印象是0f是/必须是完全可表示的(此外 – 它的二进制表示必须全为零),但在c11标准中找不到这样的声明。 certificate这一点的引用或反驳这一主张的论点将是最受欢迎的。 无论如何, 迈克尔的问题是这里的主要问题。
§5.2.4.2.2
实现可以给零和不是浮点数(例如无穷大和NaN)的值作为符号或者可以使它们无符号。
编译器无法消除浮点正零的添加,因为它不是标识操作。 按照IEEE 754规则,添加+0的结果。 到-0。 不是-0。; 它是+0。
编译器可以消除+0的减法。 或者添加-0。 因为那些是身份操作。
例如,当我编译它时:
double foo(double x) { return x + 0.; }
使用Apple GNU C 4.2.1在Intel Mac上使用-O3
时,生成的汇编代码包含添加的addsd LC0(%rip), %xmm0
。 当我编译这个:
double foo(double x) { return x - 0.; }
没有添加指令; 程序集只返回其输入。
因此,原始问题中的代码可能包含此语句的添加指令:
y[i] = y[i] + 0;
但不包含此声明的说明:
y[i] = y[i] - 0;
但是,第一个语句涉及y[i]
具有次正规值的算术,因此减慢程序就足够了。
不是非常数化的零常数0.0f
,它是循环的每次迭代接近零的值。 随着它们变得越来越接近零,它们需要更高的精度来表示,因此非规范化。 在最初的问题中 ,这些是y[i]
值。
代码的慢速和快速版本之间的关键区别是语句y[i] = y[i] + 0.1f;
。 执行此行后,浮点数中的额外精度将丢失,并且不再需要表示该精度所需的非规范化。 之后, y[i]
上的浮点运算仍然很快,因为它们没有非规范化。
添加0.1f
为什么额外的精度会丢失? 因为浮点数只有这么多有效数字。 假设您有足够的存储空间用于三位有效数字,然后0.00001 = 1e-5
和0.00001 + 0.1 = 0.1
,至少对于此示例浮点格式,因为它没有空间将最低有效位存储在0.10001
。