C# 为什么这个双重算术在一台机器上给出两个不同的答案?

C# 为什么这个双重算术在一台机器上给出两个不同的答案?,c#,floating-point,C#,Floating Point,在我正在开发的一些软件的深处有一行代码 double DataNoise = StatsStuff.MeanofSquares() - average * average; 示例编号: StatsStuff.MeanofSquares() = 1.9739125181231402E-13 average = -4.3328988592605794E-07 DataNoise = 9.6511265664977283E-15 //(State1) DataNoise = 9.651126

在我正在开发的一些软件的深处有一行代码

double DataNoise = StatsStuff.MeanofSquares() - average * average;
示例编号:

StatsStuff.MeanofSquares() = 1.9739125181231402E-13  
average = -4.3328988592605794E-07
DataNoise = 9.6511265664977283E-15 //(State1)  
DataNoise = 9.6511265664977204E-15 //(State2)
如果我反复从GUI重新启动分析,该计算的结果迟早会发生变化,有时会在第一次重新运行分析时发生变化,但在切换到不同的答案之前,它通常会给出一些一致的结果(切换前的次数会显著变化)。一旦软件切换到返回第二个值,它就不会返回第一个值

我正在使用C#和Visual Studio,在装有i5 4570的Windows 7机器上进行测试,如果这对任何人都有帮助的话

我在调试和发布版本中都看到了这个问题

每次启动分析时,都会在分析方法中重新创建所有分析对象,因此不应该有任何持久性

我已经将这些值记录到计算中,它们不会改变;我还使用了
BitConverter.GetBytes()
来检查数字是否相同

我已经在网上看到了下面的问题和许多类似的文章,但它们都与两台不同机器之间的差异有关。

中的答案似乎表明,我应该能够从一台机器和指令集预期确定性行为,但我没有

如果您能解释为什么会发生这种情况和/或如何确保结果一致,我们将不胜感激

调试中的一些附加字节值:
输入:
平均数:48、51、51、18、221、19、157、190
正方形平均数:2052502002431961997561

输出:
数据噪音(状态1):192、220、244、228、126、187、5、61
DataNoise(状态2):187、220、244、228、126、187、5、61

根据,关于使用
双精度
的操作,“使用至少双量程和精度执行操作…”

这意味着可以使用扩展精度计算
statstuff.meansofsquares()
,并且此扩展精度结果可以直接用于
statstuff.meansofsquares()-average*average
。如果计算
statstuff.meansofsquares()
产生的结果在扩展精度上略有不同,则当数字仅用17位打印时,差异可能不可见

一个重要的线索是,显示的两个结果正是通过对第一个结果使用单独的
double
乘法和加法以及对第二个结果使用融合的
double
乘法和加法或
long double
算法,从显示的输入值计算得出的结果。这表明使用不同的说明来评估这两个结果。具体而言:

  • m
    为最接近1.9739125181231402E-13的
    double
  • a
    设为最接近-4.3328988592605794E-07的
    double
  • 您显示的第一个结果9.6511265664977283E-15等于计算
    double
    中的
    a*a
    ,从
    double
    中的
    m
    中减去乘积,并将结果转换为17位小数
  • 您显示的第二个结果9.6511265664977204E-15等于使用精确数学计算
    m-a*a
    ,然后四舍五入到
    double
    (与C的
    fma(a,-a,m)
    )并将结果转换为17位十进制数字的结果。它还等于在长双精度中计算
    a*a
    ,从
    m
    中减去乘积,将结果四舍五入到
    double
    ,并将其转换为17位十进制数字的结果
执行这些不同操作的唯一方法是使用不同的指令。因此这表明,
double DataNoise=statstuff.meansofsquares()
在不同的时间被编译成不同的指令。一种可能性是该语句在源代码中出现多次。另一个原因是编译器将包含它的函数内联,以便在不同的上下文中对其进行不同的编译

由于该问题没有提供可复制的示例或关于语句上下文的任何信息
double DataNoise=statstuff.meansofsquares()-average*average
,因此不可能给出明确的答案

虽然这些数字匹配一个单独的与融合的、双精度与长精度的双精度模式,这强烈表明不同的指令用于不同的结果,但仍有可能使用一个指令序列来计算
statstuff.MeanofSquares()-average*average
但表达式的输入值会有所不同,可能是由于
statstuff.meansofsquares()
计算的精度较高,在打印的有限数字中不可见。 如果您的软件是多线程的,它可能会将问题划分为子问题,并与多个线程并行执行。当这些线程返回结果时,它可能会组合这些结果,为
statstuff.meansofsquares()
生成最终结果。由于线程可能在不同的运行中以不同的顺序完成,因此结果可能以不同的顺序组合。这意味着操作中使用的数据不同,因此结果可能不同。(例如,在两位小数中,加21+4.9+90得到26+90(21+4.9正好是25.9,所以四舍五入到两位数字得到26)然后是120,但加21+90+4.9得到110(111轮到110),然后是110(110+4.9轮到110)

另一种可能是软件有一个bug,导致它使用未初始化的数据,而这些数据会影响结果

如果问题是表达式的计算方式不同,那么一个潜在的解决方法可能是将中间结果分配给
double t0 = average*average;
double t1 = StatsStuff.MeanofSquares();
double Mean = t1 - t0;