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

2024-01-05

我目前在工作中遇到一个问题,当编译器配置从调试更改为发布时,可能会导致精度损失,而编译器配置具有不同的优化级别。由于某种原因,在我们代码的其他地方,协方差矩阵(以及类似的东西)使用了非常大的值,类似于 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/ http://x86.renejeschke.de/

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


如果您正在使用

  • GCC with - 倒数数学 http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-freciprocal-math-953(直接或间接通过-funsafe-math-optimizations or -ffast-math or -Ofast)
  • 视觉工作室fp:浮点语义的快速模式 http://msdn.microsoft.com/en-us/library/aa289157%28VS.71%29.aspx#floapoint_topic10

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

1D91FF78  divsd       xmm0,mmword ptr [dinv]

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

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

从数学上来说

a / b = a * (1 / b)

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

两种不同编译器配置之间可能会损失精度 的相关文章

随机推荐