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
算法,从显示的输入值计算得出的结果。这表明使用不同的说明来评估这两个结果。具体而言:
- 设
为最接近1.9739125181231402E-13的m
值double
- 将
设为最接近-4.3328988592605794E-07的a
值double
- 您显示的第一个结果9.6511265664977283E-15等于计算
中的double
,从a*a
中的double
中减去乘积,并将结果转换为17位小数m
- 您显示的第二个结果9.6511265664977204E-15等于使用精确数学计算
,然后四舍五入到m-a*a
(与C的double
)并将结果转换为17位十进制数字的结果。它还等于在长双精度中计算fma(a,-a,m)
,从a*a
中减去乘积,将结果四舍五入到m
,并将其转换为17位十进制数字的结果double
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;