C中的浮点运算是关联的吗?

时间:2012-12-17 11:13:11

标签: c math floating-point compiler-optimization

加法在数学上保存了关联属性:

(a + b) + c = a + (b + c)

在一般情况下,此属性不适用于浮点数,因为它们表示有限精度的值。

作为优化的一部分,是否允许编译器在从C程序生成机器代码时进行上述替换?它在C标准中的确切位置在哪里?

3 个答案:

答案 0 :(得分:13)

不允许编译器执行“优化”,这将导致计算的值不同于根据抽象机器语义计算的值。

  

5.1.2.3程序执行

     

[#1]本国际的语义描述          标准描述了抽象机器的行为          哪些优化问题无关紧要。

     

[#3]在抽象机器中,评估所有表达式        由语义指定。

     

[#13]示例5浮点表达式的重新排列          由于精度的限制,往往受到限制          以及范围。实施通常不适用          加法或数学的数学关联规则          乘法,也不是分配规则,因为          舍入错误,即使没有溢出和          下溢。

在你的例子中:

(a + b) + c

甚至没有括号:

a + b + c

我们有

   +
  / \
  +  c
 / \
 a  b

并且编译器需要生成代码,就像ab相加一样,结果与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))减少为零,忽略了INFNAN的可能性。

如果我用-fassociative-math -O2进行编译,我会收到奇怪的消息:

  

警告:-fassociative-math disabled;其他选项优先

如果您不关心操作数的顺序,-funsafe-math-optimizations可以提高速度,但如果操作数的顺序很重要,可能会导致精度损失,并且您可能会丢失NANINF结果。

答案 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%的时间内是关联的。