Same Calculation, different results

Just starting out? Need help? Post your questions and find answers here.
CalamityJames
User
User
Posts: 81
Joined: Sat Mar 13, 2010 4:50 pm

Same Calculation, different results

Post by CalamityJames »

I was interested in the PureBasic routines for the Haversine (a calculation of the distances between two points on a curved surface) found here: https://www.purebasic.fr/english/viewtopic.php?t=47853. This question is not about the formula but about two ways of calculating it. The code below shows two functionally identical procedures, the first is the way any sane person would write it (and which is taken from the link), and the second which avoids any intermediate variables. They give slightly different results. The difference is only 145m in 540km so it's not a big deal. I also know that this does not indicate any fault with PureBasic, but is to do with the number of bits of accuracy used. I would appreciate any additional clarification anyone can offer. (The places in the formula are the The Royal Observatory in Greenwich and the Royal Mile in Edinburgh.)

Code: Select all

EnableExplicit
#Earth_Radius =  6371.000785
Procedure.d Haversine(PlanetRadius.d, Lat1.d, Lon1.d, Lat2.d, Lon2.d)
Protected Sin_dLat.d, Sin_dLon.d, a.d

  Sin_dLat = Sin(Radian(Lat2 - Lat1) / 2)
  Sin_dLon = Sin(Radian(Lon2 - Lon1) / 2)
  a = Sin_dLat * Sin_dLat + Sin_dLon * Sin_dLon * Cos(Radian(Lat1)) * Cos(Radian(Lat2))
 
  ProcedureReturn PlanetRadius * 2 * ATan2(Sqr(1 - a), Sqr(a))
EndProcedure

Procedure.d Haversine2(PlanetRadius.d, Lat1.d, Lon1.d, Lat2.d, Lon2.d)
 ProcedureReturn ATan2(Sqr(1 - Sin(Radian(Lat2 - Lat1) / 2) * Sin(Radian(Lat2 - Lat1) / 2) + Sin(Radian(Lon2 - Lon1) / 2) * Sin(Radian(Lon2 - Lon1) / 2) * Cos(Radian(Lat1)) * Cos(Radian(Lat2))), Sqr(Sin(Radian(Lat2 - Lat1) / 2) * Sin(Radian(Lat2 - Lat1) / 2) + Sin(Radian(Lon2 - Lon1) / 2) * Sin(Radian(Lon2 - Lon1) / 2) * Cos(Radian(Lat1)) * Cos(Radian(Lat2)))) * PlanetRadius * 2
EndProcedure

Debug Haversine(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556)
Debug Haversine2(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556)
Debug "Difference = " + StrD(Haversine(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556) - Haversine2(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556), 17)

User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Same Calculation, different results

Post by Josh »

I haven't checked to see if your two calculations are actually identical. I don't want to say that PureBasic can't do math, but it definitely has certain peculiarities where you never really know what the result will be. With only two results for one and the same calculation you are still one of the lucky ones :mrgreen:
sorry for my bad english
PeDe
Enthusiast
Enthusiast
Posts: 278
Joined: Sun Nov 26, 2017 3:13 pm

Re: Same Calculation, different results

Post by PeDe »

I think the order of the calculations is changing. If you put brackets, the result is identical.

Code: Select all

Procedure.d Haversine2(PlanetRadius.d, Lat1.d, Lon1.d, Lat2.d, Lon2.d)
  ProcedureReturn ATan2(Sqr(1 - ((Sin(Radian(Lat2 - Lat1) / 2)) * (Sin(Radian(Lat2 - Lat1) / 2)) + (Sin(Radian(Lon2 - Lon1) / 2)) * (Sin(Radian(Lon2 - Lon1) / 2)) * Cos(Radian(Lat1)) * Cos(Radian(Lat2)))), Sqr((Sin(Radian(Lat2 - Lat1) / 2)) * (Sin(Radian(Lat2 - Lat1) / 2)) + (Sin(Radian(Lon2 - Lon1) / 2)) * (Sin(Radian(Lon2 - Lon1) / 2)) * Cos(Radian(Lat1)) * Cos(Radian(Lat2)))) * PlanetRadius * 2
EndProcedure

[18:51:02] Waiting for executable to start...
[18:51:02] Executable type: Windows - x86  (32bit, Unicode, Thread, Purifier)
[18:51:02] Executable started.
[18:51:02] [Debug] 539.66501330444737
[18:51:02] [Debug] 539.66501330444737
[18:51:02] [Debug] Difference = 0.00000000000000000
[18:51:02] The Program execution has finished.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Same Calculation, different results

Post by skywalk »

It would be better if you compared your floating point results with another app?
It is not clear whether some of the implied types in the shorter procedure default to FLOAT instead of DOUBLE.
That is why we need a known comparison to make conclusions.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Same Calculation, different results

Post by wilbert »

For most floating point operations, PureBasic uses the fpu which is 80 bits internally.
Every time an intermediate result needs to be stored, it is stored as a 64 bit (double) or 32 bit (float) value so accuracy is lost.
The most accurate calculation for this formula would be to use assembly code and only store the final result.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Same Calculation, different results

Post by skywalk »

Yes, I am curious why 32-bit would still be used for an all DOUBLE calculation?
I thought Fred updated all the math/trig functions to DOUBLE?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Same Calculation, different results

Post by Little John »

wilbert wrote:For most floating point operations, PureBasic uses the fpu which is 80 bits internally.
Every time an intermediate result needs to be stored, it is stored as a 64 bit (double) or 32 bit (float) value so accuracy is lost.
It's a pity that PureBasic does not have 80 bit numeric variables (e.g. PowerBasic has them since many years).
Helle
Enthusiast
Enthusiast
Posts: 178
Joined: Wed Apr 12, 2006 7:59 pm
Location: Germany
Contact:

Re: Same Calculation, different results

Post by Helle »

PureBasic don´t use FPU-instructions, it uses the msvcrt.dll (routines without FPU).
CalamityJames
User
User
Posts: 81
Joined: Sat Mar 13, 2010 4:50 pm

Re: Same Calculation, different results

Post by CalamityJames »

Thanks to those who replied, particulary to PeDe whose observation skills are clearly above average!. The fact that the order of calculation made a difference made me wonder which of the results was closer to the right answer so I used the Casio calculator here: https://keisan.casio.com/calculator. This offers up to 130 decimal places. On my orginal post the Haversine2 function is closer than the Haversine function, a comfort to insane programmers everywhere.
Last edited by CalamityJames on Sat Apr 25, 2020 6:54 pm, edited 1 time in total.
vwidmer
Enthusiast
Enthusiast
Posts: 286
Joined: Mon Jan 20, 2014 6:32 pm

Re: Same Calculation, different results

Post by vwidmer »

not sure if you seen this post already

http://www.purebasic.fr/english/viewtop ... 13&t=47853
WARNING: I dont know what I am doing! I just put stuff here and there and sometimes like magic it works. So please improve on my code and post your changes so I can learn more. TIA
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Same Calculation, different results

Post by Michael Vogel »

Just an idea how errors could be avoided when transforming Haversine1 to Haversine2...

...let's start with some macros...

Code: Select all

Macro _slat_
	Sin(Radian(Lat2 - Lat1) / 2)
EndMacro
Macro _slon_
	Sin(Radian(Lon2 - Lon1) / 2)
EndMacro
Macro _a_
	(_slat_*_slat_ + _slon_* _slon_ * Cos(Radian(Lat1)) * Cos(Radian(Lat2)))
EndMacro

Procedure.d Haversine(PlanetRadius.d, Lat1.d, Lon1.d, Lat2.d, Lon2.d)
	ProcedureReturn PlanetRadius * 2 * ATan2(Sqr(1 - _a_), Sqr(_a_))
EndProcedure
Next step is to add an error by adding an asterisk for instance:

Code: Select all

Procedure.d Haversine(PlanetRadius.d, Lat1.d, Lon1.d, Lat2.d, Lon2.d)
	ProcedureReturn PlanetRadius * 2 * ATan2(Sqr(1 - _a_), Sqr(_a_)) *
EndProcedure
When you start the compiler now, you'll get an error message with the resulting code which can be easily copied into the Haversine2 routine (without the asterisk)...

Code: Select all

EnableExplicit
#Earth_Radius =  6371.000785
Macro _slat_
	Sin(Radian(Lat2 - Lat1) / 2)
EndMacro
Macro _slon_
	Sin(Radian(Lon2 - Lon1) / 2)
EndMacro
Macro _a_
	(_slat_*_slat_ + _slon_* _slon_ * Cos(Radian(Lat1)) * Cos(Radian(Lat2)))
EndMacro

Procedure.d Haversine(PlanetRadius.d, Lat1.d, Lon1.d, Lat2.d, Lon2.d)
	ProcedureReturn PlanetRadius * 2 * ATan2(Sqr(1 - _a_), Sqr(_a_))
EndProcedure

Procedure.d Haversine2(PlanetRadius.d, Lat1.d, Lon1.d, Lat2.d, Lon2.d)
	ProcedureReturn PlanetRadius * 2 * ATan2(Sqr(1 - (Sin(Radian(Lat2 - Lat1) / 2)*Sin(Radian(Lat2 - Lat1) / 2) + Sin(Radian(Lon2 - Lon1) / 2)* Sin(Radian(Lon2 - Lon1) / 2) * Cos(Radian(Lat1)) * Cos(Radian(Lat2)))), Sqr((Sin(Radian(Lat2 - Lat1) / 2)*Sin(Radian(Lat2 - Lat1) / 2) + Sin(Radian(Lon2 - Lon1) / 2)* Sin(Radian(Lon2 - Lon1) / 2) * Cos(Radian(Lat1)) * Cos(Radian(Lat2)))))
EndProcedure

Debug Haversine(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556)
Debug Haversine2(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556)
Debug "Difference = " + StrD(Haversine(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556) - Haversine2(#Earth_Radius, 51.476852, -0.000500, 55.950558, -3.185556), 17)
Post Reply