在计算浮点向量的点积时,灾难性取消是否是一个问题?如果是,通常如何解决?

19

我正在使用C++编写物理模拟器,对其稳健性感到担忧。我已经了解到在浮点数算术中计算两个几乎相等大小的数字之间的差异时,可能会发生灾难性取消。

当计算两个几乎正交的向量的点积时,仿真器可能会出现这种情况。

然而,我查看的参考资料仅讨论通过重新编写相关方程(例如,可以重写二次公式以消除问题)来解决该问题-但似乎不适用于计算点积?

我想知道这是否通常是物理引擎中的一个问题,以及如何解决它。


1
也许这是适合math.stackexchange的内容? - Tom Knapen
2
@TomKnapen - 虽然这个问题可能适用于math.stackexchange,但该网站的faq建议在涉及算法实现的问题上使用SO。这个问题应该在这里提出。 - Ted Hopp
对于许多应用程序而言,这并不重要。如果发生灾难性的抵消,正确点积和计算出来的点积之比可能会显著不同...但两个值都非常接近于零,并且与您正在计算的其他值相比将是微不足道的。 - Martin Bonner supports Monica
2个回答

13

常用的技巧是使累加变量的类型具有比向量本身更高的精度。

或者,可以在求和项时使用Kahan求和算法

另一种方法是使用各种阻塞点积算法来代替传统的算法。

当然,也可以将上述两种方法结合起来使用。

请注意,上述内容涉及点积的一般误差行为,而不是特定的灾难性抵消。


1
谢谢你的回答...看起来这些解决方案解决的是舍入误差而不是抵消问题。在我的例子中,我正在计算2d向量的点积,x1x2 + y1y2(其中变量为浮点数)。我认为将它们转换为双精度浮点数并不能解决抵消问题(尽管可能会使其发生的频率更低)。如果x1x2的数量级几乎与y1y2相同,那么我也不知道Kahan算法如何解决即将发生的抵消问题...它似乎更多地涉及最小化多个求和中的误差累积。 - Nick Kovac
https://dev59.com/B1MHtIcB2Jgan1zn3i_z#63665011 对一般问题有很好的概述,包括参考资料和一个与 Kahan 不同的算法(假设您的硬件提供 FMA)。 - Nemo

5
您在评论中提到需要计算x1*x2 + y1*y2,其中所有变量都是浮点数。因此,如果您使用双精度计算,就不会失去任何精度,因为双精度比浮点精度多两倍以上的位数(假设您的目标使用 IEEE-754)。
具体而言:设xx、yy是由float变量x、y表示的实数。设xxyy为它们的乘积,xy为双精度乘法x * y的结果。则在所有情况下,xxyy是由xy表示的实数。

1
+1:这听起来是正确的。如果在进行点积之前已经失去了精度,那么你无法恢复它。通过使用双精度进行计算,你至少不会引入进一步的误差,至少对于只有两个术语的情况是如此。 - Oliver Charlesworth
1
我认为问题在于原始浮点值只是实数的近似值,因此尽管在双精度下执行点积不会引入更多的舍入误差(直到将该值转换回浮点数),但仍然存在抵消问题,因为点积操作的是近似值(由于当这些原始值的误差通过减法变得主导时,灾难性抵消就会发生)- 将它们转换为双精度并不会使原始值更准确。 - Nick Kovac
1
我猜全局使用双精度浮点数会有所帮助,因为原始值将更加准确,但是只要两个向量足够接近正交,那么这个问题仍然会发生。因此,基本上全局转换为双精度浮点数可能会使问题不那么频繁出现,但似乎并不能完全避免它? - Nick Kovac
1
有一篇关于浮点运算的好论文:http://www.validlab.com/goldberg/paper.pdf 如果你在那里搜索“内积”,建议使用双精度进行乘法求和。 - brita_
1
还有一系列关于浮点数的实践文章,详细介绍了常见陷阱和相关主题: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ - brita_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接