加法在数学上保存了关联属性:
(a + b) + c = a + (b + c)
在一般情况下,此属性不适用于浮点数,因为它们表示有限精度的值。
作为优化的一部分,是否允许编译器在从C程序生成机器代码时进行上述替换?它在C标准中的确切位置在哪里?
答案 0 :(得分:13)
不允许编译器执行“优化”,这将导致计算的值不同于根据抽象机器语义计算的值。
5.1.2.3程序执行
[#1]本国际的语义描述 标准描述了抽象机器的行为 哪些优化问题无关紧要。
[#3]在抽象机器中,评估所有表达式 由语义指定。
[#13]示例5浮点表达式的重新排列 由于精度的限制,往往受到限制 以及范围。实施通常不适用 加法或数学的数学关联规则 乘法,也不是分配规则,因为 舍入错误,即使没有溢出和 下溢。
在你的例子中:
(a + b) + c
甚至没有括号:
a + b + c
我们有
+
/ \
+ c
/ \
a b
并且编译器需要生成代码,就像a
与b
相加一样,结果与c
相加。
答案 1 :(得分:6)
您可以将浮点运算与gcc选项相关联:
-funsafe-math-optimizations -O2
示例:
double test (double a, double b, double c) {
return (a + b + c) * (a + (b + c));
}
这减少为:
double temp = a + (b + c);
return temp * temp;
同样,(a + b + c) - (a + (b + c))
减少为零,忽略了INF
和NAN
的可能性。
如果我用-fassociative-math -O2
进行编译,我会收到奇怪的消息:
警告:-fassociative-math disabled;其他选项优先
如果您不关心操作数的顺序,-funsafe-math-optimizations
可以提高速度,但如果操作数的顺序很重要,可能会导致精度损失,并且您可能会丢失NAN
和INF
结果。
答案 2 :(得分:4)
C中的浮点乘法不是关联的。
In C, Floating point multiplication is not associative.
有一些证据表明这个C代码:
选择三个随机浮动值
检查a*(b*c)
是否不等于(a*b)*c
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
using namespace std;
int main() {
int counter = 0;
srand(time(NULL));
while(counter++ < 10){
float a = rand() / 100000;
float b = rand() / 100000;
float c = rand() / 100000;
if (a*(b*c) != (a*b)*c){
printf("Not equal\n");
}
}
printf("DONE");
return 0;
}
程序打印:
Not equal
Not equal
Not equal
Not equal
DONE
RUN FINISHED; exit value 0; real time: 10ms; user: 0ms; system: 0ms
<强>结论:强>
对于我的测试,三个随机选择的浮点乘法值在70%的时间内是关联的。