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

时间:2011-07-22 18:21:33

标签: c gcc floating-point

我正在使用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;
}

4 个答案:

答案 0 :(得分:13)

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

2.1无法表示为float,因此使用最接近的可表示val_f值初始化float。在二进制中,2.1是:

10.000110011001100110011001100110011001100110011001...

float有24位二进制数字,因此二进制中val_f的值为:

10.0001100110011001100110

表达式resultat = val_c + val_i + val_l + val_f计算34030 + val_f,它以单精度计算并导致另一次舍入。

  1000010011101110.0
+               10.0001100110011001100110
-----------------------------------------
  1000010011110000.0001100110011001100110
rounds to 24 digits:
-----------------------------------------
  1000010011110000.00011010

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

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

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

答案 1 :(得分:2)

谷歌大卫戈德堡的论文“每个计算机科学家应该知道什么 浮点运算“。

答案 2 :(得分:1)

float 格式只有大约6-7位数的精度。使用%7.1f 或其他合理格式,您会更好地了解结果。

答案 3 :(得分:0)

我在这里看不到任何问题。 2.1在IEEE浮点格式中没有精确的表示,因此,它将整个答案转换为具有大约6-7(正确)sig-figs的浮点数。如果您需要更高的精度,请使用双精度。