双精度理论

本文关键字:理论 双精度 | 更新日期: 2023-09-27 18:35:29

以下表达式返回false(例如在Java和C#中)

0.1 + 0.1 + 0.1 == 0.3

所以我们了解到我们总是像这样比较双打和浮点数

Math.abs(double1 - double2) < epsilon

但为什么

0.1 + 0.1 == 0.2 returns true and 
0.1 + 0.1 + 0.1 == 0.3 returns false?

我知道它与尾数有关,但我没有完全理解它。

双精度理论

点/双精度存储为二进制分数,而不是小数部分。

有些数字不能用我们的十进制表示法完全表示。例如,十进制表示法中的 1/3 是 0.3333333...在二进制表示法中也会发生同样的事情,只是无法精确表示的数字是不同的。其中包括数字1/10。二进制表示法为 0.000110011001100...

由于二进制表示法无法精确存储它,因此它以舍入的方式存储。因此你的问题。

不应该以你喜欢的方式比较双精度:0.1 + 0.1 + 0.1 == 0.3,因为你永远不知道它们在内存中的确切存储方式,你永远不会知道这种比较的结果是什么。

@msporek他的解释是正确的。以下是位级的详细情况,为什么在这两种情况下它都是假的或真的。

首先,让我们使用 IEEE 754 浮点模型手动执行0.1 + 0.1

    Dec    IEEE 754           52-bit mantisse
             ----------------------------------------------------
    0.1 =  1.1001100110011001100110011001100110011001100110011010 * 2^-4
    0.1 =  1.1001100110011001100110011001100110011001100110011010 * 2^-4
 +  -------------------------------------------------------------------
    0.2 = 11.0011001100110011001100110011001100110011001100110100 * 2^-4
        =  1.1001100110011001100110011001100110011001100110011010 * 2^-3

这是一个完美的匹配,这意味着将 0.2 转换为 IEEE 754 以及 IEEE 754 中的 0.1 和 0.1 之和按位相等。现在让我们看看:0.2 + 0.1

    Dec    IEEE 754            52-bit mantisse
             ----------------------------------------------------
    0.2 =  1.1001100110011001100110011001100110011001100110011010 * 2^-3
    0.1 =  1.1001100110011001100110011001100110011001100110011010 * 2^-4
 +  -------------------------------------------------------------------
    0.2 =  1.1001100110011001100110011001100110011001100110011010 * 2^-3
    0.1 =  0.1100110011001100110011001100110011001100110011001101 * 2^-3
 +  -------------------------------------------------------------------
    0.3 = 10.0110011001100110011001100110011001100110011001100111  * 2^-3
        =  1.00110011001100110011001100110011001100110011001100111 * 2^-2
        =  1.0011001100110011001100110011001100110011001100110100  * 2^-2
                                                              ^^^
                                                          These bits

现在,看看加法结果的最后一点:它是 100。虽然 0.3 应该有一个 011 作为最后位。(我们将通过下面的测试程序对此进行验证)。

你现在可能会认为 CPU 具有 80 位尾数的 FPU,这是正确的,我认为行为非常依赖于情况和硬件。它有可能四舍五入到 52 位精度。

使用测试程序在内存中生成 IEEE 754 表示形式的额外检查
现在用计算机做这件事的结果与我手工做的事情完全一致:

        Dec    IEEE 754            52-bit mantisse
                 ----------------------------------------------------
        0.3 =  1.0011001100110011001100110011001100110011001100110011 * 2^-2
  0.2 + 0.1 =  1.0011001100110011001100110011001100110011001100110100 * 2^-2

确实:最后三位是不同的。