加法在数学上具有关联属性:
(a + b) + c = a + (b + c)
在一般情况下,此属性不适用于浮点数,因为它们以有限精度表示值。
在从 C 程序生成机器代码作为优化的一部分时,是否允许编译器进行上述替换?它在C标准中到底在哪里说?
加法在数学上具有关联属性:
(a + b) + c = a + (b + c)
在一般情况下,此属性不适用于浮点数,因为它们以有限精度表示值。
在从 C 程序生成机器代码作为优化的一部分时,是否允许编译器进行上述替换?它在C标准中到底在哪里说?
不允许编译器执行“优化”,这将导致计算的值与根据抽象机器语义计算的值不同。
5.1.2.3 程序执行
[#1] 本国际标准中的语义描述描述了与优化问题无关的抽象机器的行为。
[#3] 在抽象机中,所有表达式都按照语义的规定进行评估。
[#13] 示例 5 由于精度和范围的限制,浮点表达式的重新排列通常受到限制。由于舍入误差,即使没有上溢和下溢,该实现通常也不能应用数学关联规则进行加法或乘法运算,也不能应用分配规则。
在您的示例中:
(a + b) + c
甚至没有括号:
a + b + c
我们有
+
/ \
+ c
/ \
a b
并且编译器需要生成代码,好像a
与 相加,b
结果与 相加c
。
您可以使浮点运算与 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 已禁用;其他选项优先
-funsafe-math-optimizations
如果您无论如何都不关心操作数的顺序,则可以提高速度,但是如果操作数的顺序很重要,则可能会导致精度损失,并且您可能会丢失NAN
结果INF
。
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% 的时间是关联的。