4

考虑以下单元测试:

// 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);

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

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

有人可以解释一下吗?

4

4 回答 4

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 条路径。

于 2013-05-28T08:44:26.080 回答
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

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

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

于 2013-05-28T09:01:25.893 回答
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;

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

于 2013-05-28T09:07:13.163 回答
0

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

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

于 2013-05-28T10:35:51.183 回答