Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 双精度数加法的交换性质_C#_.net_Double_Precision_Commutativity - Fatal编程技术网

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。