Parallel processing 这是PLINQ bug吗?

Parallel processing 这是PLINQ bug吗?,parallel-processing,task-parallel-library,plinq,parallel-extensions,Parallel Processing,Task Parallel Library,Plinq,Parallel Extensions,PLINQ输出不同于顺序处理和并行For循环的原因 我想把10000000个数字的平方根相加。。以下是3种情况的代码: 顺序for循环: 这个的输出是:21081852648.7199 现在使用PLINQ 这个的输出是:21081852648.72 为什么PLINQ输出和Parallel.For和SEQUENATION For循环之间存在差异?我强烈怀疑这是因为带双精度的算术运算并不是真正的关联运算。求和值时可能会丢失信息,丢失的确切信息取决于操作顺序 下面的示例显示了这种效果: using S

PLINQ输出不同于顺序处理和并行For循环的原因

我想把10000000个数字的平方根相加。。以下是3种情况的代码:

顺序for循环:

这个的输出是:21081852648.7199

现在使用PLINQ

这个的输出是:21081852648.72


为什么PLINQ输出和Parallel.For和SEQUENATION For循环之间存在差异?

我强烈怀疑这是因为带双精度的算术运算并不是真正的关联运算。求和值时可能会丢失信息,丢失的确切信息取决于操作顺序

下面的示例显示了这种效果:

using System;

class Test
{
    static void Main()
    {
        double d1 = 0d;
        for (int i = 0; i < 10000; i++)
        {
            d1 += 0.00000000000000001;
        }
        d1 += 1;
        Console.WriteLine(d1);

        double d2 = 1d;
        for (int i = 0; i < 10000; i++)
        {
            d2 += 0.00000000000000001;
        }
        Console.WriteLine(d2);
    }
}
在第一种情况下,我们可以多次添加非常小的数字,直到它们变得足够大,当添加到1时仍然相关

在第二种情况下,将0.00000000000000001添加到1始终只会得到1,因为在一个double中没有足够的信息来表示1.00000000000000001-因此最终结果仍然是1


编辑:我想到了另一个可能让人困惑的方面。对于局部变量,JIT编译器能够并允许使用80位FP寄存器,这意味着可以在较少信息丢失的情况下执行算术。对于实例变量来说,情况并非如此,它必须是64位的。在您的Parallel.For示例中,total变量实际上是生成类中的实例变量,因为它由lambda表达式捕获。这可能会改变结果,但这很可能取决于计算机体系结构、CLR版本等。

在平方根求和之前,通过编写一个程序对列表进行洗牌来验证。每次运行它时,它在小数点后给出不同的值。我怀疑你的意思是“交换的”而不是“结合的”:
object locker = new object();
double total ;

Parallel.For(1,10000001,
()=>0.0,
(i,state,local)=> local+Math.Sqrt(i),
(local)=>
{
  lock(locker){ total += local; }
}
);
double tot =  ParallelEnumerable.Range(1, 10000000)
                .Sum(i => Math.Sqrt(i)); 
using System;

class Test
{
    static void Main()
    {
        double d1 = 0d;
        for (int i = 0; i < 10000; i++)
        {
            d1 += 0.00000000000000001;
        }
        d1 += 1;
        Console.WriteLine(d1);

        double d2 = 1d;
        for (int i = 0; i < 10000; i++)
        {
            d2 += 0.00000000000000001;
        }
        Console.WriteLine(d2);
    }
}