【问题标题】:Possible loss of precision between two different compiler configurations两种不同的编译器配置之间可能会丢失精度
【发布时间】:2014-03-21 21:36:29
【问题描述】:

我目前在工作中遇到一个问题,当编译器配置从 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)

我查看了两种配置的反汇编,发现了一些差异(预计是因为优化量不同)。

--调试--

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

我没有真正读过反汇编,但我的理解如下: sum 移动到 xmm0,然后 xmm0 被 dinv 就地除(结果在 xmm0 因为除法就地),然后 xmm0 被移动到 sum .

正如所料,发布的反汇编是不同的。

--发布--

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

sum 赋值给 dinv 的反汇编是:

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

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

有人知道我为什么会失去精度吗? xorps的目的是什么?

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

--更新--
正如下面提到的答案,Debug 配置使用 /fp:precise,Release 配置使用 /fp:fast(使用 Microsoft Visual Studio 2013,要获得项目的构建配置设置,只需右键单击它项目,单击属性,然后导航到 C/C++)。对我来说,这导致了 1e-15 的四舍五入错误,发出或接受订单。这对我来说是个问题,因为在代码的其他地方,有些人使用了非常大的值(在 1e90 的顺序上,给出或接受订单)。为了测试目的,我为“破坏”调试配置所做的一件事是将sum /= dinv 计算分为两个步骤。首先,通过计算1.0/dinvdinv 的倒数(在下面的答案中提到这是一个不好的操作),将该结果乘以sum,并将结果放入sum。当我这样做时,我发现 Debug 和 Release 都表现不佳。

【问题讨论】:

  • 感谢编辑,我花了几分钟试图把它弄好,但它一直给我错误。 :-)
  • Xorps 就是 XOR。所以它基本上是清除xmm0寄存器。
  • 什么是DBL?
  • 对不起,我忘了说明 DBL 是什么。 typedef double DBL;
  • 如果您使用 gcc:-Ofast 忽略严格的标准合规性。 -Ofast 启用所有 -O3 优化。它还支持并非对所有符合标准的程序都有效的优化。它打开 -ffast-math 和 Fortran 特定的 -fno-protect-parens 和 -fstack-arrays。

标签: c++ floating-point precision


【解决方案1】:

如果你正在使用

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

1D91FF78  divsd       xmm0,mmword ptr [dinv]

或释放模式下的“乘法逆除法”:

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

数学

a / b = a * (1 / b)

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

【讨论】:

  • 感谢您的回答。我查看了编译器设置,发现 /fp:precise 被选为 Debug 配置, /fp:fast 被选为 Release 配置。我测试的另一件事是让自己相信精度损失是问题所在,即分两步执行sum /= dinv 操作。首先,我计算了1.0/dinv,然后将结果乘以sum。当我这样做时,Debug 和 Release 配置彼此更加一致,而且都是错误的(由于我在描述中提到的值非常大)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
  • 2016-03-05
相关资源
最近更新 更多