Search code examples
javascriptmathprecisionarbitrary-precision

How can Javascript be used to divide a circle equally so that it's correct to 1,000,000,000% zoom?


Split off from: https://stackoverflow.com/questions/31076846/is-it-possible-to-use-javascript-to-draw-an-svg-that-is-precise-to-1-000-000-000

Using floating point precision, there are rounding errors when calculating the position of points on a circle.

Example:

function pointsOnACircle (whichpoint, numberOfPoints, cx, cy, radius) {
    //Returns a point along the outside of a circle, starting at (whichpoint=1) = 0 degrees

    eachpoint = whichpoint * 2 * Math.PI /numberOfPoints;
    var thiscx = cx + (radius * Math.sin(eachpoint));
    var thiscy = cy - (radius * Math.cos(eachpoint));

    thisPoint = [ thiscx, thiscy ];

    return thisPoint;
}

If the function is run as fourthPointofSix = pointsOnACircle (4, 6, 126, 126, 63); then the points returned are

0: 71.44039956158039 1: 157.50000000000003

Calculating them with the calculator at http://www.ttmath.org/online_calculator, the numbers should be:

0: 71.440399561580365.. 1: 157.5

Similarly, if we run fifthPointofSix = pointsOnACircle (5, 6, 126, 126, 63); then the points returned are:

0: 71.44039956158034 1: 94.50000000000004

They should be:

0: 71.440399561580365... 1: 94.5

Note: the x of both points 4 and 5 should be the same, but they come back different

Is there a standard solution for solving these kinds of precise circle-based calculations?


Solution

  • Its quite possible that very large zooms might be needed. One example of these are the interactive fractal plots. For this sort of application you don't want to scale a static object you want a dynamic solution where the object is recalculated so that the precision is appropriate to the scale factor. For example in this program Scalable circle you can zoom to any level you want. This solution uses an implicit version of the equation of a circle (x-1)^2 + y^2 = 1.

    If we look at the the precision of numbers IEEE floating point double gives us 15-16 decimal digits of floating point accuracy. This should be fine for 1e7 = 1,000,000,000% zoom. The Math.sin() and Math.cos() are accurate to the last binary digit.

    Taking the values in the question and subtract the actual values 71.44039956158039 - 71.440399561580365 = 2.842170943040401e-14 now multiply this by the scale factor 1e7 gives 2.842170943040401e-7 the error is less that one millionth of a pixel. (I've uses chromes javascript console to do the calculations).

    We might start to get problems if the scale factor is 1e14 here the error is 2.8421709430404007 a couple of pixels. For that level of zoom you would want a different algorithm. The good news is that at this level of zoom a circle will be indistinguishable from a straight line. So we could just use y=m x+c. The harder bit would be finding whether a point on the circle lies in our viewing area. We start to get problems with the accuracy of specifying the radius of the circle.