为什么在一个机器上,这种双重运算会给出两个不同的答案? [英] Why does this double arithmetic give two different answers on a single machine?

查看:51
本文介绍了为什么在一个机器上,这种双重运算会给出两个不同的答案?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我正在研究的某些软件的深处,有一行代码...

In the depths of some software I'm working on there is a line of code...

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重新启动分析,则此计算的结果迟早会更改,有时是在第一次重新运行分析时更改,但是在切换到其他答案之前,它通常会给出一些一致的结果(开关明显可变之前的次数).一旦软件切换为返回第二个值,就永远不会恢复为返回第一个值.

If I relaunch the analysis from the GUI repeatedly, sooner or later the result of this calculation changes, sometimes on the first re-run of analysis, but it will usually give a few consistent results before switching to a different answer (the number of times before the switch is significantly variable). Once the software has switched to returning this second value, it never reverts to returning the first.

我正在使用C#和Visual Studio,如果使用i5 4570,则可以在Windows 7计算机上进行测试,

I'm using C# and Visual Studio, testing on a Windows 7 machine with an i5 4570 if that helps anyone.

我在Debug和Release版本中都看到了这个问题.

I have seen the problem in both Debug and Release builds.

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

Each time the analysis is launched all the analysis objects are recreated within the analysis method, so there shouldn't be anything persisting.

我已经记录了要计算的值,并且它们没有改变;我还使用了 BitConverter.GetBytes()来检查数字是否相同.

I've logged the values going into the calculation and they do not change; I have also used BitConverter.GetBytes() to check the numbers are the same.

我已经在网上看到了以下问题以及许多其他类似的文章,但是它们都与两台不同机器之间的差异有关.
为什么此浮点计算会给出不同的结果结果...

I've already seen the question below and many other articles like it online, but they all relate to the differences between two different machines.
Why does this floating point calculation give different results...

how-deterministic-is-floating-point-inaccuracy 似乎建议我应该能够从单个机器和指令集中获得确定性的行为,但我没有.

The answers in how-deterministic-is-floating-point-inaccuracy seem to suggest that I should be able to expect deterministic behaviour from a single machine and instruction set, yet I don't.

任何帮助解释这种情况发生的原因和/或如何确保结果一致的帮助,将不胜感激.

Any help explaining why this happens and/or how to ensure a consistent result would be greatly appreciated.

一些来自调试的附加字节值:
输入:
平均:48,51,51,18,221,19,157,190
MeanOfSquares:205、250、200、243、196、199、75、61

Some additional byte values from debugging:
Inputs:
average: 48, 51, 51, 18, 221, 19, 157, 190
MeanOfSquares: 205, 250, 200, 243, 196, 199, 75, 61

输出:
DataNoise(状态1):192、220、244、228、126、187、5、61
DataNoise(状态2):187,220,244,228,126,187,5,61

Outputs:
DataNoise (state 1): 192, 220, 244, 228, 126, 187, 5, 61
DataNoise (state 2): 187, 220, 244, 228, 126, 187, 5, 61

推荐答案

根据 C#规范,关于使用 double 的操作,该操作至少要使用两倍范围和精度…"

According to the C# specification, regarding an operation with double, "the operation is performed using at least double range and precision…"

这意味着 StatsStuff.MeanofSquares()可以以扩展精度计算,并且该扩展精度结果可以直接用于 StatsStuff.MeanofSquares()-平均值*平均值.如果 StatsStuff.MeanofSquares()的计算得出的结果在扩展精度上略有不同,则当仅打印17位数字时,差异可能看不到.

This means that StatsStuff.MeanofSquares() could be computed with extended precision, and this extended precision result could be used directly in StatsStuff.MeanofSquares() - average * average. If the computation of StatsStuff.MeanofSquares() produces results that vary slightly in extended precision, the differences may not be visible when the number is printed with only 17 digits.

一个明显的线索是,显示的两个结果恰好是通过对第一个结果使用单独的 double 乘法和加法以及所融合的 double所显示的输入值计算得出的结果乘法和加法或 long double 算法用于第二个结果.这表明正在使用不同的指令来评估这两个结果.具体来说:

A significant clue is that the two results displayed are exactly the results one could get by calculating from the input values shown using separate double multiply and addition for the first result and either fused double multiplication and addition or long double arithmetic for the second result. This suggests that different instructions are being used to evaluate the two results. Specifically:

  • m 为最接近1.9739125181231402E-13的 double 值.
  • a 为最接近-4.3328988592605794E-07的 double 值.
  • 您显示的第一个结果9.6511265664977283E-15等于在 double 中计算 a * a 的结果,从 m 在 double 中,并将结果转换为17个十进制数字.
  • 您显示的第二个结果9.6511265664977204E-15等于使用精确数学计算 ma * a 的结果,然后四舍五入为 double (与C的 fma(a,-a,m))并将结果转换为17个十进制数字.它也等于在 long double 中计算 a * a 的结果,从 m 中减去乘积,将结果四舍五入为 double ,并将其转换为17个十进制数字.
  • Let m be the double value that is closest to 1.9739125181231402E-13.
  • Let a be the double value that is closest to -4.3328988592605794E-07.
  • The first result you show, 9.6511265664977283E-15, equals the result of computing a*a in double, subtracting the product from m in double, and converting the result to 17 decimal digits.
  • The second result you show, 9.6511265664977204E-15, equals the result of computing m-a*a with exact mathematics and then rounding to double (as with C’s fma(a, -a, m)) and converting the result to 17 decimal digits. It also equals the result of computing a*a in long double, subtracting the product from m, rounding the result to double, and converting that to 17 decimal digits.

执行这些不同操作的唯一方法是通过不同指令.因此,这表明 double DataNoise = StatsStuff.MeanofSquares()在不同的时间被编译为不同的指令.一种可能是该语句在源代码中多次出现.另一个是编译器会内联包含该函数的函数,以便在不同的上下文中对其进行不同的编译.

The only way that these different operations would be performed is by different instructions. So this suggests that double DataNoise = StatsStuff.MeanofSquares() is being compiled to different instructions at different times. One possibility is that this statement appears multiple times in the source code. Another is that the compiler inlines the function containing it, so that it is compiled differently in different contexts.

由于该问题没有提供可复制的示例或有关语句 double DataNoise = StatsStuff.MeanofSquares()-平均值*平均值的上下文的任何信息,因此无法给出明确的答案.

Since the question does not provide a reproducible example or any information about the context of the statement double DataNoise = StatsStuff.MeanofSquares() - average * average, no definitive answer is possible.

虽然数字匹配单独的对融合数字和双重对长双精度模式,这强烈表明不同的指令用于不同的结果,但仍有可能使用一个指令序列来计算 StatsStuff.MeanofSquares()-平均值*平均值,但表达式的输入值有所不同,可能是由于 StatsStuff.MeanofSquares()以额外的精度计算出来的,而该精度在有限的数字中不可见你打印了.如果您的软件是多线程的,则可能会将问题划分为多个子问题,并与多个线程并行执行.当这些线程返回结果时,它可能会组合结果以生成 StatsStuff.MeanofSquares()的最终结果.由于线程可能会在不同的运行中以不同的顺序完成,因此结果可能会以不同的顺序组合.这意味着在操作中使用了不同的数据,因此结果可能会有所不同.(例如,以两位十进制表示,将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).

While fact that the numbers match a separate-versus-fused and a double-versus-long-double pattern, which strongly suggest different instructions are used for different results, the possibility remains that one instruction sequence is used to calculate StatsStuff.MeanofSquares() - average * average but that the input values to the expression vary, likely with StatsStuff.MeanofSquares() calculated with extra precision that is not visible in the limited digits you printed. If your software is multithreaded, it may partition a problem into subproblems and execute them in parallel with multiple threads. As those threads return results, it may combine the results to produce a final result for StatsStuff.MeanofSquares(). Since the threads may complete in different orders in different runs, the results may be combined in different orders. This means different data is being used in the operations, so the results may be different. (For example, in two-digit decimal, adding 21 + 4.9 + 90 yields 26 + 90 (21 + 4.9 is exactly 25.9, so rounding to two digits produces 26) and then 120, but adding 21 + 90 + 4.9 yields 110 (111 rounds to 110) and then 110 (110+4.9 rounds to 110).

另一种可能性是该软件存在一个错误,导致该错误使用未初始化的数据,并且该数据会影响结果.

Another possibility is the software has a bug that causes it to use uninitialized data, and this data affects the results.

如果问题在于使用不同的方式对表达式进行求值,则可能的解决方法是将中间结果分配给临时变量:

If the problem is that the expression is being evaluated in different ways, a potential workaround might be to assign intermediate results to temporary variables:

double t0 = average*average;
double t1 = StatsStuff.MeanofSquares();
double Mean = t1 - t0;

我猜想,这样的赋值将导致将每个表达式四舍五入为 double .我没有在C#规范中看到对此的明确声明,但这是C语言中的一条规则,并且C#编译器也可以这样做.如果是这样,这可能会大大降低观察到不同最终结果的频率,但可能不会完全消除它们.

I conjecture that such assignments will result in rounding each expression to double. I do not see an explicit statement of this in the C# specification, but it is a rule in C, and the C# compiler may do it as well. If so, this will likely considerably reduce the frequency with which different final results are observed, but it may not completely eliminate them.

(由于C#不能满足您的目的,因此应让Microsoft知道,并应寻求其他语言和其他编译器.)

(Since C# is not serving your purposes, you should let Microsoft know, and you should seek other languages and other compilers.)

这篇关于为什么在一个机器上,这种双重运算会给出两个不同的答案?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆