考虑以下单元测试:
// 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。 ..
有人可以解释一下吗?
答案 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值。