【问题标题】:Unexpected Output when adding two float numbers添加两个浮点数时的意外输出
【发布时间】:2011-09-10 05:15:40
【问题描述】:

我编写了以下 C++ 代码:

float a, b;
int c;

a = 8.6;
b = 1.4;
c = a + b;

printf("%d\n", c);

输出是10

但是当我运行以下代码时:

float a, b;
int c;

a = 8.7;
b = 1.3;
c = a + b;

printf("%d\n", c);

输出为9

两者之间有什么区别,因为它们提供不同的输出?

【问题讨论】:

    标签: c++ c


    【解决方案1】:

    浮点数中没有 8.7 或 1.3 这样的数字。有数字 10、数字 -6.5 和数字 0.96044921875……但没有 8.7 或 1.3。

    您的计算机最多可以将 8.7 舍入到最接近的浮点数,也可以将 1.3 舍入到最接近的浮点数。计算机将这些四舍五入的数字相加,然后将结果四舍五入。

    不要使用浮点数来赚钱。

    #include <stdio.h>
    int main(int argc, char *argv[])
    {
        float a = 8.7, b = 1.3;
        printf("Looks like: %.1f + %.1f = %.1f\n", a, b, a+b);
        printf("The truth: %.20f + %.20f = %.20f\n", a, b, a+b);
        return 0;
    }
    

    在 x86 GCC/Linux 计算机上,我得到了结果:

    看起来像:8.7 + 1.3 = 10.0 真相:8.69999980926513671875 + 1.29999995231628417969 = 9.99999976158142089844

    在 PPC GCC/OS X 计算机上,我得到了结果:

    看起来像:8.7 + 1.3 = 10.0 真相:8.69999980926513671875 + 1.29999995231628417969 = 10.00000000000000000000

    请注意,在这种特殊情况下,8.7 和 1.3 是如何向下舍入的。如果您选择四舍五入的数字,您可能会在右侧看到大于 10 的数字。

    请参阅 What Every Computer Scientist Should Know About Floating-Point Arithmetic,作者 David Goldberg (link)。

    【讨论】:

    • 不要使用 binary 浮点数。十进制浮点数已针对金融应用进行了精确标准化。
    • @Pascal Cuoq:不应该使用定点小数来赚钱吗?在某些地方,这种算术是法律规定的……
    【解决方案2】:

    浮点数与实数不同,它们的行为也大不相同。

    实数是无限的,而浮点数是有限的,只能表示所有可能实数的一小部分。

    由于并非所有实数都可以表示为浮点数,因此浮点赋值或运算可能会得到与实数空间中相同的结果略有不同的结果。

    有关介绍,请参阅wikipedia entry on floating point。关于floating point accuracy 的部分特别有趣,并提供了与您的类似的其他示例。

    【讨论】:

      【解决方案3】:

      两者之间没有真正的区别。它们的行为方式都是不可预测的。

      你所做的相当于抛硬币两次,然后问你做了什么不同的事情,一次是正面,另一次是反面。并不是你做了什么不同的事情,而是当你掷硬币时会发生这种情况。

      如果您要求一个人使用 6 位小数精度将三分之一和三分之二相加,然后向下舍入为整数,您可能会得到 0,也可能会得到 1。这取决于它们是否代表 2/3作为“0.666666”或“0.6666667”,它们都是可以接受的。所以 0 和 1 都是可以接受的答案。如果您不准备接受任何一个答案,请不要问这种问题。

      【讨论】:

      • 在浮点中,0.666667 是 2/3 的唯一可接受的表示形式,带有 6 个十进制数字。某些运算(+、-、*、/、sqrt)要求在精确答案的 1/2 ULPS 以内,而 0.666666 不满足计算 2/3 值的标准。
      • 我说的不是浮点数,我说的是 6 位十进制精度,它是一种定点。 (这是一个类比。)
      • 我也在谈论 6 位小数。 1/2 ULP 表示计算 2/3 的结果必须在 2/3 的 1/2 x 10^-6 之间,这可以通过学校教授的常用舍入方法来满足。唯一不确定结果的情况是最后一个为 5 的情况,例如,0.5 可以根据您选择的规则四舍五入为 0 或 1(向上舍入、向下舍入、舍入为零、从零,向偶数舍入)。但是,没有一组可接受的规则导致 2/3 舍入为 0.666666。
      • 这样的话,如果你取2/3,再减去1/3两次,再乘以一百万,就会发生不好的事情。重点不是要陷入细节中,而是要说明为什么会出现“错误”的输出。
      • 但是如果你取 2/3 并减去 1/3 两次,在 IEEE 二进制浮点中,无论精度如何,你总是保证得到正好 0。我的观点是,如果您不了解规则,结果只会出乎意料。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-23
      • 2013-09-18
      • 1970-01-01
      • 2021-08-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多