I am making a function that calculates the distance of two points using their latitude/longitude (in degrees not radians) and the spherical law of cosines. The problem I have is that due to rounding errors in the function acos()
the results I am obtaining are far from good when the two points are very close to each other.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct{
double lat;
double lon;
} point;
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define EARTH_RADIUS_IN_METERS 6372797.560856
double distance(point a, point b) {
double arg=sin(a.lat * DEG_TO_RAD) * sin(b.lat * DEG_TO_RAD) + cos(a.lat * DEG_TO_RAD) * cos(b.lat * DEG_TO_RAD) * cos((a.lon-b.lon) * DEG_TO_RAD);
if(arg>1) arg=1;
else if (arg<-1) arg=-1;
printf("arg=%.12f acos(arg)=%.12f\n",arg, acos(arg)); //to see the problem
return acos(arg) * EARTH_RADIUS_IN_METERS;
}
int main(){
point p1,p2;
p1.lat=63.0;
p1.lon=27.0;
p2.lat=p1.lat;
p2.lon=p1.lon;
printf("dist=%.8f\n",distance(p1,p2));
return 0;
}
The output is
arg=1.000000000000 acos(arg)=0.000000014901
dist=0.09496208
as you can see, when it computes the acos()
it should give zero but it does give some errors that get enormously magnified after multiplying by the earth radius. It also happens when the two points are not equal but very close. If it is of any use my data about latitude and longitude has up to 7 decimal digits.
The result you get from
acos
is as good as it gets: the problem is that the calculation ofarg
will always have a small error and return a value that is slightly off. When the two points are equal or very close the result is less than one, for example 1-10-16. If you look at the graph ofacos(x)
you'll see that it's nearly vertical at x=1, which means that even the slightest error inarg
has a huge impact on the relative error. In other words, the algorithm is numerically unstable.You can use the haversine formula to get better results.