0

Im working on one of a PIC24F series microcontroller to calculate distance between two latitude and longitude cordinates. I've included "math.h" header file for PIC24F in the code. The problem is acos(1) gives "0.000345" value, but im expecting "0.000086". The same code i've worked in eclipse by using eclipse's math.h header file, it give correct value "0.000086". What is the problem? Is "acos()" differ for each math.h header file?

.....
theta = lon1 - lon2;
printf("%f",theta);
dist = sin(deg2rad(lat1)) * sin(deg2rad(lat2)) + cos(deg2rad(lat1)) * cos(deg2rad(lat2)) * cos(deg2rad(theta));
printf("%f", dist);
dist = acos(dist);
printf("%f", dist);
dist = rad2deg(dist);
printf("%f", dist);
.....

After passing the coordinates, the each "dist" variable value is below

Eclipse Output:

 0.006082
 1.000000
 0.000086
 0.004939

Microcontroller Output:

0.006088 
1.000000
0.000345
0.019782     

Thanks

user6161
  • 279
  • 4
  • 17
  • 2
    Just asking, are you really using nested functions? – Sourav Ghosh Aug 10 '15 at 07:00
  • 3
    Huh ? `acos(1)` should be 0.0, no? Please post an [MCVE](http://stackoverflow.com/help/mcve) rather than just the code you happen to be working on. – Paul R Aug 10 '15 at 07:02
  • @SouravGhosh Hi, im using three seperate functions inside a main function. – user6161 Aug 10 '15 at 07:18
  • 3
    @user25770: [acos(1) == 0](http://www.wolframalpha.com/input/?i=acos%281%29). Not 0.000345, and not 0.000086. I smell a code problem here. – DevSolar Aug 10 '15 at 07:19
  • Why did you delete the code? – weston Aug 10 '15 at 08:00
  • @weston Here i posted the code. Pls check it. – user6161 Aug 10 '15 at 08:04
  • 2
    Have you tried `printf("%f", acos(1f));`? and print these to a higher number of places: `printf("%.20f", dist);` As per potatoswatter's answer, you probably will see it is not exactly `1f` – weston Aug 10 '15 at 08:07
  • Hi @weston. How i pass "1f" in acos function? It gives an error. – user6161 Aug 10 '15 at 08:33
  • Sorry `printf("%f", acos(1.0f));` or just `printf("%f", acos(1));` – weston Aug 10 '15 at 08:42
  • @weston Perhaps you mean `printf("%f", acosf(1));` – Potatoswatter Aug 10 '15 at 08:49
  • @Potatoswatter I've tried to print the exact floating point of 'dist = sin(deg2rad(lat1)) * sin(deg2rad(lat2)) + cos(deg2rad(lat1)) * cos(deg2rad(lat2)) * cos(deg2rad(theta)); printf("%.10f", dist);' I used **"%.10f"**. Im getting in eclipse's "0.9999999963" and in PIC "0.9999999404". – user6161 Aug 10 '15 at 09:08
  • 1
    @user25770: Ah... you meant `acos`, as on `man 1 acos`... not `acos( 1 )` as in `acos( 1.0 )`. In that case, the existing answers are spot-on. [Floating point maths is never precise](http://stackoverflow.com/questions/588004); and between different implementations running on different hardware, accrued rounding errors etc. your numbers will slowly diverge when you put them through consecutive function calls like you did. – DevSolar Aug 10 '15 at 13:36

2 Answers2

6

The GNU library, and any other that attempts conformance to floating-point standards, will return zero for acos(1). Live example.

Most likely, the argument is not exactly 1 but slightly smaller. acos(1 - FLT_EPSILON/2), passing the last single-precision number before 1, yields 0.00034526 which is your result. Perhaps the PIC, being a very low-power device, does not implement double precision math. No smaller (but nonzero) acos result is possible in single-precision arithmetic.

Besides that, the rounding of floating-point results, which is the source of calculation errors, may vary between platforms.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • FLT_EPSILON is the distance from 1 to the NEXT double which is TWICE as big as the the distance to the previous, because 1 is the point of an exponent jump!!! For that reason what you mean is `acos(1 - 0.5 * FLT_EPSILON)` which is 0.0000000149012. 0.5 is the inverse radix. – Coolwater Aug 10 '15 at 07:56
  • @Coolwater I think that's the `DBL_EPSILON` result, but good catch! Now things line up nicely. – Potatoswatter Aug 10 '15 at 08:01
  • 1
    PIC24 is at the higher end of the PIC family and according to [Microchip](http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en552209) it does support double precision. Probably some options need to be enable to link the large floating-point library http://www.microchip.com/forums/m633037.aspx – phuclv Aug 10 '15 at 08:18
2

The problem is that trigonometry is hard. Precise algorithms are either very slow or very large, fast algorithms are either very large or very imprecise, and small algorithms are either very slow or very imprecise. Large systems can get away with large implementations, but embedded systems, with their smaller resources and lower speed, usually have to make do with less precise results.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358