Mathematical precision of Arm chips

I was wanting to do some higher precision maths calculations for GPS using Vincentys and Haversine formula on a microcontroller. Ideally I would want a minimum 10 decimal places precision.

Out of the ARM chips available in the Arduino-type family, which would be the best for this type of application?

M0+ (Arduino Zero)
M3 (Arduino Due)
M4 (Teensy 3.1)
or any others that you know of?

Or is the limiting factor the Arduino/AVR-gcc core?

P.S. what is the precision of the Atmega 8 bit micros coupled with the Arduino IDE?

ATMega's use 32-bit floats, with 6-7 decimal points of precision.

On the due, they're 64-bits.

DrAzzy:
On the due, they're 64-bits.

Just double? Or both float and double?

just double, afaik, sorry.

lemming:
Or is the limiting factor the Arduino/AVR-gcc core?

Well, yes, it is the GCC "core".

It of course has nothing to do with the chip itself - other than the fact that very few actual microcontrollers have a Floating Point Coprocessor. Any chip will perform whatever maths you program it to do. :grinning:

Eventually.

Any chip will perform whatever maths you program it to do.

Does this mean that I will get exactly the same answer out to, say, 10 decimal places or further no matter whether I run the calculation on any of the above micros or an Atmega328? (some taking a lot longer than others of course).

Also would this answer be exactly the same as that obtained from a 64 bit PC or would the micro compiler limit the accuracy?

With the proper software math library you get the same floating point result on any of those mcus (for specified precison - single (32bit) or double (64bit). It has nothing to do with the chip itself but the math software (the math lib must fit into its flash memory, of course).

The single precision (32bit) floating point is 6-7 digits precise, the double precision (64bit) floating point is 14-15digits precise.

There is a "navspark arduino compatible" chip with built-in 64bit hw FPU (@100MHz clock), for example, so if you want great speed and GPS stuff it is a good candidate for you..
p.

The DUE supports both float(32) and double(64)

float x = PI;
double y = PI;

void setup()
{
  Serial.begin(115200);
  Serial.println("Start ");
  Serial.print("x:  ");
  Serial.print(x, 20);
  Serial.print(" ");
  Serial.println(sizeof(x));

  Serial.print("y:  ");
  Serial.print(y, 20);
  Serial.print(" ");
  Serial.println(sizeof(y));
}

void loop(){}

Start
x: 3.14159274101257324218 4
y: 3.14159265358979311599 8

PI 3.14159265358979323846264338327950288419716939937510582097494459230781640.....

float has 24 bit significand ==> 24 *log 2 digits = ~7 digts
double has 53 bit significand ==> 53 * log 2 digits = ~16 digits

You see in the test sketch that double PI starts its inaccuracy at digit 17

When talking trigonometric functions - the precision vary for various math library implementations. There is a good test for it called 9degree test:

double 9degree( void ) {

#define torad(x) x*3.1415926535897932384626433832795/180.0
#define todeg(x) x*180.0/3.1415926535897932384626433832795

   double temp = (double) 9.0;

   temp = sin(torad(temp));
   temp = cos(torad(temp));
   temp = tan(torad(temp));
   temp = todeg(atan(temp));
   temp = todeg(acos(temp));
   temp = todeg(asin(temp));

   return temp;
}

When targeting "meter" precision with haversine you definitely need double precision at least. Even so with short distances (ie. around 10meters) between two points on Earth double result will not be more precise than 3 digits.
:slight_smile:

Btw you may try float vs. double with Haversine:

// Haversine formula for distance[m] and initial bearing[degree]
// of 2 points on Earth based on LAT[degree] and LON[degree]
// Pito 6/2015

#define R 6371000.0
#define PI 3.1415926535897932384626433832795

// lat and long in decimal degree, distance in meters
double distance (double lat1, double lon1, double lat2, double lon2) {
double phi1 = lat1 * PI / 180.0;
double phi2 = lat2 * PI / 180.0;
double dphi = (lat2 - lat1) * PI / 180.0;
double dlambda = (lon2 - lon1)* PI / 180.0;
double a1 = sin(dphi/2.0)*sin(dphi/2.0);
double a2 = cos(phi1)*cos(phi2);
double a3 = sin(dlambda/2.0)*sin(dlambda/2.0);
double a = a1 + (a2 * a3);
double c = 2.0 * atan2(sqrt(a), sqrt(1.0-a));
double d = R * c;
return d;
}

double bearing (double lat1, double lon1, double lat2, double lon2) {
double phi1 = lat1 * PI / 180.0;
double phi2 = lat2 * PI / 180.0;
double dlambda = (lon2 - lon1) * PI / 180.0;
double y = sin(dlambda)*cos(phi2);
double x = cos(phi1)*sin(phi2)-sin(phi1)*cos(phi2)*cos(dlambda);
double brn = (atan2(y,x)) * 180.0 / PI;
return brn;
}

// lat1  lon1   lat2     lon2 (degree)
// 50.0 15.0 50.0001 15.0001   (13.2185m, 32.73222deg)
// 50.0 15.0 -60.0   -170.0    (18859.128km, 166.025deg)

P.

The closest thing to an Arduino with hardware floating point capabilities is probably the TI "Tiva Launchpad", which has an ARM CM4 CPU WITH the floating point hardware, and is supported by Energia, which is an arduino-clone IDE and environment. However, the hardware FP unit only supports single precision (32bit) floats, and I'm not sure to what extent the arm-gcc they use uses the FP hardware to implement "double."

The AVR 32bit float support is highly optimized for the AVR, while I think that most ARM environments use the generic gnu floating point libraries, which are .. generic and rather slow.