两种不同编译器配置之间可能会丢失精度

时间:2014-03-20 11:14:37

标签: c++ floating-point precision

我目前遇到的问题是,当编译器配置从Debug更改为Release时,可能会导致精度损失,这些问题具有不同的优化级别。出于某种原因,在我们的代码中的其他地方,非常大的值已被用于协方差矩阵(以及那种类型的东西),值在1e90的某处。我遇到的问题是,无论何时计算中存在任何精度损失,并且这些极大值中的一个仍然存在,两者的乘积都会引入一些不稳定性。我不确定为什么没有使用更合理的价值,但我不是那个编写这个代码的人,所以是的...截至目前,我相信我已经将问题追溯到一个特定的位置。我在该位置的确切数字如下所示:

DBL sum = 6.000000040000000400e-004; // same for debug and release configurations
const DBL dinv = 2.000000020000000300e-004; // same for debug and release configurations

请注意,DBL是普通的双倍:

typedef double DBL;

然后,执行以下操作:

sum /= dinv;

这会产生:

sum = 2.999999990000000100e+000 // (for debug configuration)<br>
sum = 2.999999989999999600e+000 // (for release configuration)

我看了两个配置的反汇编,发现了一些差异(预计会有不同的优化量)。

- DEBUG -

1D91FF73  movsd       xmm0,mmword ptr [sum]
1D91FF78  divsd       xmm0,mmword ptr [dinv]
1D91FF7D  movsd       mmword ptr [sum],xmm0

我还没有真正阅读过反汇编,但我的理解如下:sum被移动到xmm0,然后xmm0被divv分割就位(结果是xmm0,因为除法就位),然后xmm0被移动总结。

正如预期的那样,释放的反汇编是不同的。

- RELEASE -

1D7557AB  movsd       xmm1,mmword ptr [esp+50h]  
1D7557B1  xorps       xmm0,xmm0  
1D7557B4  mulsd       xmm1,mmword ptr [esp+68h]  

将总和分配给dinv的反汇编是:

1D7B55B7  movsd       xmm1,mmword ptr [esp+68h]  

我认为dinv是由[esp + 68h]表示的指针指向的值,而sum是由[esp + 50h]表示的指针指向的值吗?如果不是,那是什么情况?

有人知道我失去精确度的原因吗? xorps的目的是什么?

此链接中的x86指令集参考可能会有所帮助:http://x86.renejeschke.de/

- UPDATE -
正如下面提到的答案,Debug配置使用/ fp:precise,而Release配置使用/ fp:fast(使用Microsoft Visual Studio 2013,获取项目的构建配置设置,只需右键单击该项目项目,单击属性,然后导航到C / C ++)。对我来说,这导致了1e-15的顺序错误,给出或接受订单。这对我来说是个问题,因为在代码的其他地方,有些人使用了非常大的值(大约1e90,给出或接受订单)。我做过的一件事是&#34;打破&#34;用于测试目的的Debug配置是将sum /= dinv计算拆分为两个步骤。首先,通过计算dinv1.0/dinv的倒数(这被称为在下面的答案中执行的不良操作),将结果乘以sum,并将结果放入sum。我发现当我这样做时,Debug和Release都表现不佳。

1 个答案:

答案 0 :(得分:1)

如果您正在使用

编译器可能会在调试模式下生成标准除法指令:

1D91FF78  divsd       xmm0,mmword ptr [dinv]

或&#34;除数乘以&#34;在发布模式:

1D7557B4  mulsd       xmm1,mmword ptr [esp+68h]

在数学上

a / b = a * (1 / b)

但在现实世界中,乘以倒数总是会引入更多错误,并且不允许编译器执行此优化,因为结果会有所不同且不符合(wrt IEEE-754)。