K&R的C语言有以下句子:
编译器将数学关联操作符视为计算关联操作符的许可被吊销。
这是在附录C中,它告诉我们与ANSI C之前有什么不同。但是我不知道计算关联与数学关联有什么不同。也许我猜数学上的结合法是a * b * c = (a * b) * c(左),或者a * (b * c)(右)。
考虑以下代码:
#include <stdio.h>
int main(void)
{
double a = 0x1p64; // Two the power of 64, 18,446,744,073,709,551,616.
double b = 1;
double c = -a;
printf("%gn", a+b+c);
}
在C语法中,a+b+c
等价于(a+b)+c
,所以先添加a
和b
,再添加c
。在double
通常使用的格式中,a+b
的结果是264,而不是264+1,因为double
格式没有足够的精度来表示264+1,所以加法的结果是理想的数学结果,四舍五入到最接近的可表示值,即264。然后加上c
得到0,因此输出" 0 "。
如果我们计算a+c+b
,将a
和c
相加将得到0,然后将b
相加将得到1,并且将打印" 1 "。
因此,浮点操作通常不是关联的;a+b+c
和a+c+b
不一样
在普通实数数学中,a+b+c等于a+c+b;实数的加法是结合法
在标准化之前,一些C编译器会将浮点表达式视为关联运算符(对于那些实数算术中的对应运算符是关联运算符的操作符)。C标准不允许在符合标准的实现中这样做。符合标准的编译器产生的结果必须与操作按照C语法指定的顺序执行一样。
在非标准模式下操作时,一些编译器可能仍然将浮点操作符视为关联操作符,非标准模式可以通过传递给编译器的标志或开关来选择。此外,由于C标准允许实现以比标称类型更高的精度执行浮点运算(例如,当计算a+b+c
时,它可以像计算long double
而不是double
一样计算它),这可以产生与重新排列操作相同的结果,因此您仍然可以得到看起来像操作符已经关联重新排序的结果,这取决于C实现和使用的标志。