当前位置: C语言 -- 专题 -- 浮点算术运算的不可结合性

浮点算术运算的不可结合性

结合律(associative law)是一个数学定律,是指计算结果不会因为计算顺序的不同而发生改变,例如:

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

(a × b) × c = a × (b × c)


遵循IEEE 754标准的具体实现中,绝大多数浮点数都只能近似的表示,其精确程度取决于精度。由于精度和值域范围的限制,浮点表达式的重排经常会受到限制。由于舍入误差(roundoff error),即使在没有溢出(overflow)和下溢(underflow)的情况下,具体实现中浮点表达式通常也不能使用加法或者乘法的数学结合律(associative law)。以下是一个范例:

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
/*浮点运算的不可结合性。*/

#include <float.h>
#include <math.h>
#include <stdio.h>

int main(void)
{
    printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD);

    printf("%f\n", (2.0f + powf(2.0f, 25.0f)) - powf(2.0f, 25.0f));	//A
    printf("%f\n", 2.0f + (powf(2.0f, 25.0f) - powf(2.0f, 25.0f)));

    return 0;
}

将输出(注:使用ideone编译。):

FLT_EVAL_METHOD = 0

0.000000

2.000000


FLT_EVAL_METHOD会影响float类型的浮点操作和浮点常量的评估,其值由具体实现定义。当宏FLT_EVAL_METHOD值为0时,将根据float类型的范围和精度评估所有float类型的浮点操作和浮点常量;当宏FLT_EVAL_METHOD值为1时,将根据double类型的范围和精度评估所有float类型的浮点操作和浮点常量;当宏FLT_EVAL_METHOD值为2时,将根据long double类型的范围和精度评估所有float类型的浮点操作和浮点常量。

上面的例子中,A处的输出仅在宏FLT_EVAL_METHOD值为0时,输出0.000000;如果宏FLT_EVAL_METHOD值为1或者2时,将输出2.000000


powf(2.0f, 25.0f)返回float类型的2.025.02.0+2.025.0的值为1.0000 0000 0000 0000 0000 0001×225

C语言中的float类型数据在支持IEEE 754标准的计算机上以32位二进制格式表示。

2.025.032位二进制格式表示:

0 1001 1000 0000 0000 0000 0000 0000 000

1.0000 0000 0000 0000 0000 0001×22532位二进制格式表示:

0 1001 1000 0000 0000 0000 0000 0000 000

两者表示形式相同;当数据类型是float类型时,1.0000 0000 0000 0000 0000 0001×225小数点后的1无法表示。


综上所述,通常情况下可认为浮点算术运算不具有可结合性。


主要参考资料:

1、ISO/IEC 9899:2018

2、《深入理解计算机系统》原书第3版 作者:Randal E. Bryant David R. O'Hallaron 出版社:机械工业出版社

3、IEEE Std 754-2019