具有双精度数的加法的交换性质

时间:2013-05-28 08:37:36

标签: c# .net double precision commutativity

考虑以下单元测试:

// Works (sum 0.1 to 0.4)
float f1 = 0.1F + 0.2F + 0.3F + 0.4F;
Assert.AreEqual(1F, f1);

// Works too (sum 0.4 to 0.1)
float f2 = 0.4F + 0.3F + 0.2F + 0.1F;
Assert.AreEqual(1F, f2);

// Works (sum 0.1 to 0.4)
double d1 = 0.1D + 0.2D + 0.3D + 0.4D;
Assert.AreEqual(1D, d1);

// Fails! (sum 0.4 to 0.1)
double d2 = 0.4D + 0.3D + 0.2D + 0.1D;
Assert.AreEqual(1D, d2);

浮动类型的一切都按预期工作(两种情况下的总和都是1),但是当使用double时,加法的交换性不受尊重。 实际上,第一个的总和是1,但是对于第二个,我得到0.99999999 .....

我理解为什么结果只有1次而一次没有(因为有些数字不能在不损失基数2的精度的情况下表示)但这并不能解释为什么它适用于float而不是double。 ..

有人可以解释一下吗?

4 个答案:

答案 0 :(得分:3)

看看下面的

        // This works (sum 0.1 to 0.4)
        double d1 = 0.1D + 0.2D + 0.3D + 0.4D;
        double d11 = 0;
        d11 += 0.1D;//0.1
        d11 += 0.2D;//0.30000000000000004
        d11 += 0.3D;//0.60000000000000009
        d11 += 0.4D;//1.0

        // This does NOT work! (sum 0.4 to 0.1)
        double d2 = 0.4D + 0.3D + 0.2D + 0.1D;
        double d22 = 0;
        d22 += 0.4D;//0.4
        d22 += 0.3D;//0.7
        d22 += 0.2D;//0.89999999999999991
        d22 += 0.1D;//0.99999999999999989

调试,看看各个步骤。

你需要记住的是

        double d2 = 0.4D + 0.3D + 0.2D + 0.1D;

也可以被视为

        double d2 = (((0.4D + 0.3D) + 0.2D) + 0.1D);

这个问题似乎不是数字1的2个表示形式,而是它到达目的地的2条路径。

答案 1 :(得分:2)

float f11 = 0;
f11 += 0.1F;//0.1
f11 += 0.2F;//0.3
f11 += 0.3F;//0.6
f11 += 0.4F;//1.0

float f2 = 0.4F + 0.3F + 0.2F + 0.1F;
float f22 = 0;
f22 += 0.4F;//0.4
f22 += 0.3F;//0.700000048
f22 += 0.2F;//0.900000036
f22 += 0.1F;//1.0

添加到 astander 的答案 - 这是值查找浮点数的方式。由于精度较低(浮点数为7位,双打数为14-15),最终显示的值不同,并且意外地与您的预期相同。

但就是这样 - 这只是巧合!永远不要依赖它。浮点运算是关联的,也不是精确的。永远不要使用==比较浮点数或加倍,始终考虑使用一些保证金值。此示例适用于1,但对于其他值,它将失败。

答案 2 :(得分:1)

以下内容:

float f = 0.3F + 0.3F + 0.2F + 0.1F;
double d = 0.3D + 0.3D + 0.2D + 0.1D;

结果将是:

float f = 0.900000036f;
double d = 0.9;

因此,对于不同的数字,舍入错误可能发生在浮点数中,其中双精度数中没有,反之亦然 - 这是因为它们具有不同的位数,因此可能发生舍入错误的位置不同。

答案 3 :(得分:0)

这是比较浮点数时的一个已知问题,因为根据C#规范,它们是基于导致此行为的令人讨厌的IEEE标准实现的。

所以你永远不应该在C#中比较2 float或double。相反,你应该看看他们的差异是否小于特定的delta值。