为什么GCC在添加浮点值时会产生意外结果?

lig*_*ght 3 c floating-point gcc

我正在使用GCC编译一个添加浮点数,长整数,整数和字符的程序.当它运行时,结果很糟糕.以下程序意外打印值34032.101562.

使用Microsoft编译器重新编译可以得到正确的结果.

#include <stdio.h>

int main (void) {
    const char val_c = 10;
    const int val_i = 20;
    const long val_l = 34000;
    const float val_f = 2.1;

    float result;
    result = val_c + val_i + val_l + val_f;

    printf("%f\n", result);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*non 13

您认为"正确的结果"是什么?我猜你相信它是34032.1.事实并非如此.

2.1不能表示为a float,因此val_f使用最接近的可表示float值进行初始化.在二进制文件中,2.1是:

10.000110011001100110011001100110011001100110011001...
Run Code Online (Sandbox Code Playgroud)

a float有24个二进制数字,所以val_f二进制的值是:

10.0001100110011001100110
Run Code Online (Sandbox Code Playgroud)

表达式resultat = val_c + val_i + val_l + val_f计算34030 + val_f,以单精度计算并导致另一个舍入发生.

  1000010011101110.0
+               10.0001100110011001100110
-----------------------------------------
  1000010011110000.0001100110011001100110
rounds to 24 digits:
-----------------------------------------
  1000010011110000.00011010
Run Code Online (Sandbox Code Playgroud)

在十进制中,此结果正好是34032.1015625.因为%f格式在小数点后打印6位数(除非另有说明),所以再次舍入,并打印printf 34032.101562.

现在,为什么在使用MSVC编译时没有得到这个结果?如果编译器选择这样做,C和C++标准允许以更宽的类型执行浮点计算.MSVC通过您的计算执行此操作,这意味着结果34030 + val_f在传递之前未被舍入printf.在这种情况下,打印的确切浮点值为34032.099999999991268850862979888916015625,由printf舍入为34032.1.

为什么不是所有的编译器都做MSVC的工作呢?有几个原因.首先,它在某些处理器上速度较慢.其次,更重要的是,虽然它可以提供更准确的答案,但程序员不能依赖于此 - 看似无关的代码更改可能导致答案在出现此行为时发生变化.因此,携带额外的精度往往会导致比解决更多的问题.