Decimal Precision

Hello,

I'm working on a project for which I need maximum accuracy, i.e. preferably up to 6 decimal place.

Therefore for a following code:

double x = 7.342567;
double y = 3.783765;
double z = x/y;
Serial.println(z);

I want the output to be:

z = 1.940545

However when I input the code in Arduino I only get z = 1.94. How can I correct this?

Thanks

The default, when printing a double or float is to print 2 decimal places. There is an optional argument to the print function, when the first argument is a real, to define the number of decimal places (at least on version 18).

Where are the values to be printed coming from? Are they really accurate to 6 decimal places?

I am using version 18

I want to store up to 6 decimal places so that I can use them for future calculations. The values to be printed are coming from a GPS unit which I have stored into double values and them used them from complex calculations for which I need more accuracy that 2 decimal places.

If you want to take a look at it. My code is listed below:

if(strstr(stat,statusA)!=NULL)
      {
          double JD, UT, T, M_prime, M, L0_prime, L0, DL_prime, DL, L_prime, L, eps, eps_prime;
      double X, Y, Z, R, RA_hrs, RA_deg, delta, delta_prime;
            double GST_prime, GST, theta, tau, ha, ALT, AZ, AZ_prime;
            char hh[3], ss[3], mm[3], LatDeg[3], LatMin[8], dirNS[2], LongDeg[4], LongMin[6], dirEW[2], dd[3], mo[3], yy[3];

        memset(hh,'\0',3);            
        memset(ss,'\0',3);
        memset(mm,'\0',3);
        memset(LatDeg,'\0',3);
        memset(LatMin,'\0',8);
        memset(dirNS,'\0',2);
        memset(LongDeg,'\0',4);
        memset(LongMin,'\0',6);
        memset(dirEW,'\0',2);
        memset(dd,'\0',3);
        memset(mo,'\0',3);
        memset(yy,'\0',3);
        
                strncpy(hh, linea +8, 2);
            double Hour = atof(hh);
            
                strncpy(mm, linea +10, 2);
            double Min = atof(mm);

            strncpy(ss, linea +12, 2);
            double Sec = atof(ss);

            strncpy(LatDeg, linea +17, 2);
            double Lat_deg = atof(LatDeg);

            strncpy(LatMin, linea +19, 7);
            double Lat_min = atof(LatMin);

            strncpy(dirNS, linea +27, 1);

            strncpy(LongDeg, linea +29, 3);
            double Long_deg = atof(LongDeg);

            strncpy(LongMin, linea +32, 5);
            double Long_min = atof(LongMin);

            strncpy(dirEW, linea +40, 1);

            strncpy(dd, linea +54, 2);
            double Day = atof(dd);
            


            strncpy(mo, linea +56, 2);
            double Month = atof(mo);

            strncpy(yy, linea +58, 2);
            double Year = atof(yy) + 2000.0;



            // Calculating Latitude and Longtitude in decimal values
             double Long = Long_deg + (Long_min/60.0);
             double Lat = Lat_deg + (Lat_min/60.0);

            // Adjusting Latitude and Longtitude vales based on N/S and E/W direction
            char testNS[] = "N";
            char test2NS[] = "S";
            if(strstr(dirNS,testNS)!=NULL)
            {
                  Lat = Lat * 1.0;
            }
            else if(strstr(dirNS,test2NS)!=NULL)
            {
                  Lat = Lat * -1.0;
            }

            char testEW[] = "E";
            char test2EW[] = "W";
            if(strstr(dirEW,testEW)!=NULL)
            {
                  Long = Long * 1.0;
            }
            if(strstr(dirEW,test2EW)!=NULL)
            {
                  Long = Long * -1.0;
            }

            // Calculating Time Zone
            double TimeZone;
            if (Long < 0.0)
            {
                  TimeZone = ceil(Long/15.0);
            }
            else if (Long > 0.0)
            {
                  TimeZone = floor(Long/15.0);
            }

            if (TimeZone == -0.0)
            { 
                  TimeZone = 0.0;
            }

            // Converting Gregorian calendar date to Julian Date
            JD = 367.0*Year;
            JD -= floor(7.0*(Year+floor((Month+9.0)/12.0))/4.0);
            JD += floor(275.0*Month/9.0);
            JD += Day;
            JD += (Hour + (Min + Sec/60.0)/60.)/24.0;
            JD += 1721013.5;
            T = (JD-2451545.0) / 36525.0;

            // Calculating UT based on Time Zone and given Time
            //UT = (Hour - TimeZone) + (Min/60.0) + (Sec/3600.0);
            UT = Hour + Min + Sec;

            // Solar Coordinates Calculation, accuracy of 0.01 degree
            double k = (2.0*PI)/360.0;

            // mean anomaly, degree
            M_prime = 357.52910 + 35999.05030*T - 0.0001559*T*T - 0.00000048*T*T*T;
            if (M_prime > 0.0)
            {   
                  if (M_prime < 360.0)
                  {
                        M = M_prime;
                  }   
                  else if (M_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(M_prime/360.0);
                        M = M_prime - (360.0*reminder);
                  }
            }
            else if (M_prime < 0.0)
            {
                  if (M_prime > -360.0)
                  {
                        M = 360.0 - abs(M_prime);
                  }
                  else if (M_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(M_prime)/360.0);
                        M = 360.0 - (abs(M_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  M = 0.0;
            }

            // mean longitude, degree
            L0_prime = 280.46645 + 36000.76983*T + 0.0003032*T*T;
            if (L0_prime > 0.0)
            {   
                  if (L0_prime < 360.0)
                  {
                        L0 = L0_prime;
                  }   
                  else if (L0_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(L0_prime/360.0);
                        L0 = L0_prime - (360.0*reminder);
                  }
            }
            else if (L0_prime < 0.0)
            {
                  if (L0_prime > -360.0)
                  {
                        L0 = 360.0 - abs(L0_prime);
                  }
                  else if (L0_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(L0_prime)/360.0);
                        L0 = 360.0 - (abs(L0_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  L0 = 0.0;
            }

            DL_prime = (1.914600 - 0.004817*T - 0.000014*T*T)*sin(k*M) + (0.019993 - 0.000101*T)*sin(k*2.0*M) + 0.000290*sin(k*3.0*M);
            if ( DL_prime > 0.0)
            {   
                  if (DL_prime < 360.0)
                  {
                        DL = DL_prime;
                  }   
                  else if (DL_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(DL_prime/360.0);
                        DL = DL_prime - (360.0*reminder);
                  }
            }
            else if (DL_prime < 0.0)
            {
                  if (DL_prime > -360.0)
                  {
                        DL = 360.0 - abs(DL_prime);
                  }
                  else if (DL_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(DL_prime)/360.0);
                        DL = 360.0 - (abs(DL_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  DL = 0.0;
            }

            // true longitude, degree
            L_prime = L0 + DL;
            if ( L_prime > 0.0)
            {   
                  if (L_prime < 360.0)
                  {
                        L = L_prime;
                  }   
                  else if (L_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(L_prime/360.0);
                        L = L_prime - (360.0*reminder);
                  }
            }
            else if (L_prime < 0.0)
            {
                  if (L_prime > -360.0)
                  {
                        L = 360.0 - abs(L_prime);
                  }
                  else if (L_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(L_prime)/360.0);
                        L = 360.0 - (abs(L_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  L = 0.0;
            }

            // convert ecliptic longitude L to right ascension RA and declination delta
            // (the ecliptic latitude of the Sun is assumed to be zero):

            // obliquity eps of ecliptic:
            eps_prime = 23.0 + 26.0/60.0 + 21.448/3600.0 - (46.8150*T + 0.00059*T*T - 0.001813*T*T*T)/3600.0;
            if (eps_prime > 0.0)
            {   
                  if (eps_prime < 360.0)
                  {
                        eps = eps_prime;
                  }   
                  else if (eps_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(eps_prime/360.0);
                        eps = eps_prime - (360.0*reminder);
                  }
            }
            else if (eps_prime < 0.0)
            {
                  if (eps_prime > -360.0)
                  {
                        eps = 360.0 - abs(eps_prime);
                  }
                  else if (eps_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(eps_prime)/360.0);
                        eps = 360.0 - (abs(eps_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  eps = 0.0;
            }
            double eps2 = 23.439 - 0.013*T;

            X = cos(L*k);
            Y = cos(eps*k)*sin(L*k);
            Z = sin(eps*k)*sin(L*k);
            R = sqrt(1.0-Z*Z);

            delta_prime = atan(Z/R) * 1/k; // in degrees
            if (delta_prime > 0.0)
            {   
                  if (delta_prime < 360.0)
                  {
                        delta = delta_prime;
                  }   
                  else if (delta_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(delta_prime/360.0);
                        delta = delta_prime - (360.0*reminder);
                  }
            }
            else if (delta_prime < 0.0)
            {
                  if (delta_prime > -360.0)
                  {
                        delta = 360.0 - abs(delta_prime);
                  }
                  else if (delta_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(delta_prime)/360.0);
                        delta = 360.0 - (abs(delta_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  delta = 0.0;
            }

            // calculating the declination angle in degrees
            double dec = asin(Z) * 1.0/k;

            // calculating the right ascension (RA) in hours and degrees
            RA_hrs = (24.0/180.0) * (atan(Y/(X+R)) * 1.0/k); // in hours
            RA_deg = atan(Y/X) * 1.0/k;
// adjusting the right ascension (RA) angle
            if (X < 0) /*Make sure we're in the right quadrant*/
            {
                  RA_deg = RA_deg + 180;
            }
            else if ((X > 0) && (Y < 0))
            {
                  RA_deg = RA_deg + 360;
            }
            else 
            {
                  RA_deg = 360 + RA_deg;
            }    
            if (RA_deg > 360)
            {           
                  RA_deg = RA_deg - 360;

            }

            // compute sidereal time at Greenwich
            GST_prime = 280.46061837 + 360.98564736629*(JD-2451545.0) + 0.000387933*T*T - (T*T*T)/38710000.0;
            if (GST_prime > 0.0)
            {   
                  if (GST_prime < 360.0)
                  {
                        GST = GST_prime;
                  }   
                  else if (GST_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(GST_prime/360.0);
                        GST = GST_prime - (360.0*reminder);
                  }
            }
            else if (GST_prime < 0.0)
            {
                  if (GST_prime > -360.0)
                  {
                        GST = 360.0 - abs(GST_prime);
                  }
                  else if (GST_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(GST_prime)/360.0);
                        GST = 360.0 - (abs(GST_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  GST = 0.0;
            }

            theta = GST + 10.0;
            tau = theta - RA_deg;
            // ha = L0 - RA_deg + 180.0 + (15.0*UT) + Long;
            // ha = 15.0*(12.0-Hour);

            // calculating local sid. time angle
            double LocST_prime, LocST;
            LocST_prime = GST - (360.0 - Long);
            if (LocST_prime > 0.0)
            {   
                  if (LocST_prime < 360.0)
                  {
                        LocST = LocST_prime;
                  }   
                  else if (LocST_prime > 360.0)
                  {
                        double reminder;
                        reminder = floor(LocST_prime/360.0);
                        LocST = LocST_prime - (360.0*reminder);
                  }
            }
            else if (LocST_prime < 0.0)
            {
                  if (LocST_prime > -360.0)
                  {
                        LocST = 360.0 - abs(LocST_prime);
                  }
                  else if (LocST_prime < -360.0)
                  {
                        double reminder;
                        reminder = floor(abs(LocST_prime)/360.0);
                        LocST = 360.0 - (abs(LocST_prime) - (360.0*reminder));
                  }
            }
            else
            {
                  LocST = 0.0;
            }

            // calculating the local hour angle
            ha = LocST - RA_deg;
            if (ha < 0)
            { 
                  ha = ha + 360;
            }
            // double hau = 68.33;
            // Calculating the final Altitude angle
            ALT = asin(sin(Lat*k)*sin(dec*k) + cos(Lat*k)*cos(dec*k)*cos(ha*k)) * 1.0/k;

That is basically the core of the code... and the output is as follows in the next post

This is the output that we get.

$GPGSV,3,3,12,23,68,269,36,29,01,028,00,31,20,096,00,32,06,203,00*74
$GPRMC,004739,A,4356.5357,N,07853.5767,W,000.0,030.7,110410,,,A*60


---------------
 
 
GPS INPUTS: 
Day:  11.00
Month: 4.00
Year: 2010.00
Hour: 0.00
Min: 47.00
Sec: 39.00
Latitude: 43.94
Longtitude: -78.89
Time Zone: -5.00
 
MICROCONTROLLER OUTPUTS:
JD: 2455297.5000000
T2000: 0.10
Longitude: 21.01
Declin.: 8.20
Right Asc.: 19.41
Local Sid. Time: 120.23
Local Hour Angle: 100.82
Altitude: -2.00
Azimuth: 283.40

So even if we extend the amount of decimal places we do not get the accurate reading that we'd like.

For example, the JD value is to read:
JD 2455297.54167

and not what we're getting

2455297.5000000

Also, just to point out. The JD value that we're getting checks out with our code because we ran this on C and it works there perfectly fine. Help will be appreciated.

// Converting Gregorian calendar date to Julian Date
            JD = 367.0*Year;
            JD -= floor(7.0*(Year+floor((Month+9.0)/12.0))/4.0);
            JD += floor(275.0*Month/9.0);
            JD += Day;
            JD += (Hour + (Min + Sec/60.0)/60.)/24.0;
            JD += 1721013.5;

What is the type for Year, Month, Day, Hour, Min, and Sec?

For example, the JD value is to read:
JD 2455297.54167

and not what we're getting

2455297.5000000

Printing some intermediate values may give you a clue as to what is happening, but I'm not sure that 5 decimal places of precision is reasonable for a date where the finest resolution is in seconds.

All those variable are declared as doubles

interesting that you guys posted on this today. I want to display about 10 decimals of precision (and yes, they will eventually be precise)

double intVar = integrate(a,b,N);
Serial.println(intVar,10);

arduino results

N:4.00  integration: 4999998.0000000000
N:8.00  integration: 2500000.5000000000
N:16.00  integration: 1249998.7500000000
N:32.00  integration: 624997.7500000000
N:64.00  integration: 312497.2187500000
N:128.00  integration: 156246.9531250000
N:256.00  integration: 78121.8515625000
N:512.00  integration: 39058.7617187500
N:1024.00  integration: 19531.3554687500
N:2048.00  integration: 9767.3535156250
N:4096.00  integration: 4885.4858398437
N:8192.00  integration: 2442.0256347656
N:16384.00  integration: 1222.3886718750
N:32768.00  integration: 612.0148925781
N:65536.00  integration: 304.8067321777
N:131072.00  integration: 153.6506958007
N:262144.00  integration: 77.9761810302
N:524288.00  integration: 40.1629104614

microsoft visual studio c++ (2008?)

       4     4999998.046915
       8     2500000.878394
      16     1249998.748414
      32      624997.735664
      64      312497.237285
     128      156246.989892
     256       78121.866633
     512       39059.305113
    1024       19531.280733
    2048        9767.212049
    4096        4885.177452
    8192        2441.017346
   16384        1222.079582
   32768         612.610659
   65536         304.734468
  131072         153.938045
  262144          78.539828
  524288          40.840718

the math coding is identical. all calculations are done with doubles

I want to display about 10 decimals of precision (and yes, they will eventually be precise)

The Arudino only supports 4 byte floating-point values (float). You will get 6-7 digits of precision; never 10.

all calculations are done with doubles

No, they aren't. For the Arduino float and double are the same type.

so if i do all the calculations with 'doubles' then i'm only using 1 variable type, which is double, right?

english isn't my first language

No, they aren't.  For the Arduino float and double are the same type.

I'm not understanding what you're trying to point out. Ok so double and float are the same, but you're pointing this out to me why?

either way it's obvious that the arduino run above isn't nearly accurate to 6 decimals. I'll look over the code but i don't think 6 sigfig accuracy in the integration would result in numbers differing so much

Ok so double and float are the same, but you're pointing this out to me why?

You might expect that with about 6 digits of precision in a "float", you would get about 12 digits of precision with a double. With avr gcc (which is what is underneath arduino), this won't be true. Changing variables to "double" form "float" won't do anything at all.

is there any variable that can get over 6sigfigs on arduino?

I'm not understanding what you're trying to point out. Ok so double and float are the same, but you're pointing this out to me why?

Try this... Change all doubles in your Microsoft C program to floats. Leave the Arduino program the way it is. Compare the output from the two programs.

is there any variable that can get over 6sigfigs on arduino?

Probably not. It's an 8 bit processor that doesn't have a multiply or divide. It just isn't designed to perform floating-point operations.

is there any variable that can get over 6sigfigs on arduino?

You may be able to using fixed-point datatypes.

There is a discussion here about adding fixed-point to AVR-GCC...
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=73345

thanks for all the posts on this topic. However, I still am looking for somebody to inform me of how i can get greater precision on my code. For example, making the varaible JD to have more precision.

However, I still am looking for somebody to inform me of how i can get greater precision on my code. For example, making the varaible JD to have more precision

2455297.5000000

Eight digits. As far as I know, that's the best you'll be able to do with a float (or double) on the Arduino.

A long (32 bit integer) has more precision than a float (almost 9 digits!)
I have been told that avr-gcc supports "long long" (64 bit integers), which is even better (twice as many digits!)

How to do input and output of 64bit numbers is not exactly clear.

How to do "fixed point" math using integer variables is ... somewhat complex.

It would probably be easier to find a more complete floating point or arbitrary precision library that someone else has written.

There is still no one who told us simply and exactly how to make more than two digits after the "," . I need more exact precision too. I got "0,04" as an variable output number and need if possible "0,0357" this should be possible right?
Please just tell me how to make that happen :slight_smile:

Florian

There is still no one who told us simply and exactly how to make more than two digits after the "," . I need more exact precision too. I got "0,04" as an variable output number and need if possible "0,0357" this should be possible right?
Please just tell me how to make that happen

You've already been told, by 'thefatmoop'. Look back at his example, or read the second paragraph of the Serial print documentation: Serial.print() - Arduino Reference

!c

Good night

Hello Coder0091, did you arrived to a conclusion?
I'm on the same boat...

Please let me know
Thanks on advance
Best regards
Pedro Ferrer