C# 双精度数加法的交换性质
考虑以下单元测试:C# 双精度数加法的交换性质,c#,.net,double,precision,commutativity,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
// 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.9999999
我理解为什么结果一次为1,一次不为1(因为有些数字在基数2中无法表示而不损失精度),但这并不能解释为什么它适用于float而不适用于double…
有人能解释一下吗?看看下面的内容
// 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的两个表示,而是它如何到达那里的两条路径
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
,但对于其他值,它肯定会失败。如下所示:
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;
因此,对于不同的数字,舍入误差可能会出现在浮点数中,而双精度浮点数中没有舍入误差,反之亦然——这是因为它们的位数不同,因此舍入误差可能出现的位置不同。这是比较浮点数时的已知问题,因为根据C#规范,它们是基于导致这种行为的恶劣IEEE标准实施
所以你永远不应该在C#中比较2浮点或双精度。相反,您应该查看它们的差值是否小于特定的增量值。通常,不应该期望与浮点相关。然而,至于为什么你的测试会这样,我不确定。众所周知,根据你添加浮点数的顺序,你会得到不同的结果。特别是,为了获得最大的精度,您应该从最小值到最大值进行排序和添加,以最大限度地减少舍入误差。还有一个问题是,为什么单精度和双精度之间存在差异?嗯,float和double有不同的舍入误差,所以它们可以给出不同的结果。其实就这么简单。另一种观点是,两个数字相加通常会产生与较大数字成比例的舍入误差;如果将0.1到0.2到0.3到0.4相加,则只有一次相加将涉及大于0.5的数字。如果将0.4添加到0.3,然后将结果添加到0.2,然后将结果添加到0.1,则其中两个添加将涉及大于0.5的数字。此外,0.1+0.2+0.3+0.4的折算形式为0.4+(0.3+(0.2+0.1)),可能等于0.1+0.2+0.3+0.4。