11

我正在尝试诊断和修复一个错误,该错误归结为 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()的函数。它计算多边形的质心,但是当多边形很小时,质心会在该位置周围不规则地跳跃,并且最终可能与多边形相距很远。上面的数据系列是在一致的方向上移动多边形的一个节点的结果(用手,这就是它不完全平滑的原因)。

这是 Adob​​e Flex 4.5。

4

2 回答 2

19

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

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

如果您的多边形非常小,则lxlon几乎相同,就像ly和一样latp。与结果相比,它们都非常大,因此您要减去两个几乎相等的数字。

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

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;

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

于 2012-05-06T13:09:52.953 回答
3

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

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

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

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

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

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

于 2012-09-04T08:03:39.060 回答