【问题标题】:How to divide tiny double precision numbers correctly without precision errors?如何正确划分微小的双精度数而不会出现精度错误?
【发布时间】:2012-05-06 12:43:27
【问题描述】:

我正在尝试诊断和修复一个错误,该错误归结为 X/Y 在 X 和 Y 很小时会产生不稳定的结果:

在这种情况下,cx 和 patharea 都平滑增加。它们的比率在大数时是平滑渐近线,但对于“小”数则不稳定。显而易见的第一个想法是我们正在达到浮点精度的极限,但实际数字本身却远不及它。 ActionScript“数字”类型是 IEE 754 双精度浮点数,因此应该有 15 个十进制数字的精度(如果我没看错的话)。

分母(patharea)的一些典型值:

0.0000000002119123
0.0000000002137313
0.0000000002137313
0.0000000002155502
0.0000000002182787
0.0000000002200977
0.0000000002210072

还有分子(cx):

0.0000000922932995
0.0000000930474444
0.0000000930582124
0.0000000938123574
0.0000000950458711
0.0000000958000159
0.0000000962901528
0.0000000970442977
0.0000000977984426

每一个都是单调增加的,但如上所示,比率是混乱的。

在较大的数字上,它会稳定为平滑的双曲线。

那么,我的问题是:当您需要将一个数除以另一个时,处理非常小的数字的正确方法是什么?

我想提前将分子和/或分母乘以 1000,但无法完全解决。

有问题的实际代码是recalculate() 函数here。它计算多边形的质心,但是当多边形很小时,质心会在该位置周围不规则地跳跃,并且最终可能与多边形相距很远。上面的数据系列是多边形的一个节点沿一致的方向移动的结果(用手,这就是为什么它不是完全平滑的)。

这是 Adob​​e Flex 4.5。

【问题讨论】:

  • 乘以 1000,除以结果除以 1000 时发生了什么?
  • 好吧,没什么好 :) 在当前的一个化身中,我最终得到了一个只有两位精度的商。在这一点上,感觉就像我真的在通过反复试验进行编码,因此我的目标是接受一些关于正确做事方式的教育。
  • 另外 - 你会乘分子还是分母?我猜是后者?

标签: actionscript-3 floating-point double division floating-accuracy


【解决方案1】:

我认为问题很可能是由您的代码中的以下行引起的:

sc = (lx*latp-lon*ly)*paint.map.scalefactor;

如果您的多边形非常小,那么 lxlon 几乎相同,lylatp 也是如此。与结果相比,它们都非常大,因此您要减去两个几乎相等的数字。

为了解决这个问题,我们可以利用以下事实:

x1*y2-x2*y1 = (x2+(x1-x2))*y2 - x2*(y2+(y1-y2))
            = x2*y2 + (x1-x2)*y2 - x2*y2 - x2*(y2-y1)
            = (x1-x2)*y2 - x2*(y2-y1)

那么,试试这个:

dlon = lx - lon
dlat = ly - latp
sc = (dlon*latp-lon*dlat)*paint.map.scalefactor;

数值在数学上是相同的,但项要小一个数量级,因此误差也应该小一个数量级。

【讨论】:

  • 天才。绝对解决问题。你应该得到比我微薄的赞成票和“正确答案”勾号更多的权利。
  • 顺便说一句,您(或某人)能否详细说明您所说的“错误”是什么意思?通过减少值之间的大小差异及其差异,我们是否可以更好地利用 64 位浮点数的有限空间?这发生在编译器级别还是芯片级别?
  • 哦,其实我明白了。 lx 和 latp 大约为 100。将它们相乘,大约为 10,000(丢失了两个有效数字的十进制精度)。现在我明白为什么乘以 1000 没有任何变化。
【解决方案2】:

Jeffrey Sax 已正确识别出基本问题 - 组合比最终结果(远)大的术语会导致精度损失。 建议的重写消除了部分问题 - 考虑到满意的响应,对于实际情况显然足够了。

但是,您可能会发现,如果多边形再次(大大)变小和/或远离原点,则会再次出现不准确性。在重写后的公式中,这些项仍然比它们的差值大很多。

此外,算法中还有另一个“组合大数字和可比数字与不同符号”的问题。在多边形边缘上的后续迭代循环中的各种“sc”值有效地组合成一个最终数字,该数字比单个 sc(i) 小得多。 (如果你有一个凸多边形,你会发现有一个连续的正值序列和一个连续的负值序列,在非凸多边形中,负数和正数可能交织在一起。

该算法实际上所做的是通过添加由边和原点跨越的三角形区域来计算多边形的面积,其中一些项是负数(每当顺时针遍历边时,从原点)和一些积极的(逆时针走在边缘)。

您可以通过在多边形的一个角(例如 (lx,ly) 处定义原点,然后添加由边缘和该角跨越的三角形表面(因此:转换lon 到 (lon-lx) 和 latp 到 (latp-ly) - 额外的好处是您需要减少处理两个三角形,因为显然链接到所选原点角的边产生零表面。

对于区域部分,仅此而已。对于质心部分,您当然必须将结果“转换回”到原始帧,即在末尾添加 (lx,ly)。

【讨论】:

  • 感谢您的补充说明。
猜你喜欢
  • 2013-06-20
  • 2019-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
  • 1970-01-01
  • 2013-01-11
相关资源
最近更新 更多