如何正确分割微小的双精度数字没有精度错误? [英] How to divide tiny double precision numbers correctly without precision errors?

查看:128
本文介绍了如何正确分割微小的双精度数字没有精度错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我正在尝试诊断并修复一个错误,归结为X / Y,当X和Y小时,会产生不稳定的结果: https://i.stack.imgur.com/GFYOn.pngalt =enter image description here>



在这种情况下,cx和patharea都增加顺利。他们的比例是高数量的平滑渐近线,但对于小数来说是不稳定的。明显的第一个想法是,我们达到了浮点精度的极限,但实际数字本身就不在其附近。 ActionScriptNumber类型是IEE 754双精度浮点数,因此应该有十五进制数精度(如果我正确阅读)。



分母(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() function 这里。它计算多边形的质心,但是当多边形很小时,质心在该地方不规则地跳动,并且可以从多边形结束长距离。上面的数据系列是以一致的方向移动多边形的一个节点(用手,这就是为什么它不完全平滑)的结果。



这是Adobe Flex

解决方案

我相信这个问题最有可能是由你的代码中的以下代码引起的:

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

如果您的多边形非常小,那么 lx lon 几乎相同, ly latp 。与结果相比,它们都非常大,所以你减去两个几乎相等的数字。



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

  x1 * y2-x2 * y1 =(x2 +(x1-x2))* y2  -  x2 *(y2 + 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;

该值在数学上是相同的,但是这些术语是一个数量级更小的值,所以错误应该也要小一个数量级。


I'm trying to diagnose and fix a bug which boils down to X/Y yielding an unstable result when X and Y are small:

In this case, both cx and patharea increase smoothly. Their ratio is a smooth asymptote at high numbers, but erratic for "small" numbers. The obvious first thought is that we're reaching the limit of floating point accuracy, but the actual numbers themselves are nowhere near it. ActionScript "Number" types are IEE 754 double-precision floats, so should have 15 decimal digits of precision (if I read it right).

Some typical values of the denominator (patharea):

0.0000000002119123
0.0000000002137313
0.0000000002137313
0.0000000002155502
0.0000000002182787
0.0000000002200977
0.0000000002210072

And the numerator (cx):

0.0000000922932995
0.0000000930474444
0.0000000930582124
0.0000000938123574
0.0000000950458711
0.0000000958000159
0.0000000962901528
0.0000000970442977
0.0000000977984426

Each of these increases monotonically, but the ratio is chaotic as seen above.

At larger numbers it settles down to a smooth hyperbola.

So, my question: what's the correct way to deal with very small numbers when you need to divide one by another?

I thought of multiplying numerator and/or denominator by 1000 in advance, but couldn't quite work it out.

The actual code in question is the recalculate() function here. It computes the centroid of a polygon, but when the polygon is tiny, the centroid jumps erratically around the place, and can end up a long distance from the polygon. The data series above are the result of moving one node of the polygon in a consistent direction (by hand, which is why it's not perfectly smooth).

This is Adobe Flex 4.5.

解决方案

I believe the problem most likely is caused by the following line in your code:

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

If your polygon is very small, then lx and lon are almost the same, as are ly and latp. They are both very large compared to the result, so you are subtracting two numbers that are almost equal.

To get around this, we can make use of the fact that:

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)

So, try this:

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

The value is mathematically the same, but the terms are an order of magnitude smaller, so the error should be an order of magnitude smaller as well.

这篇关于如何正确分割微小的双精度数字没有精度错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆