Math operation with double and float numbers

Hello, and good morning.
I am programming on Arduino Nano to make a solar follower to orientate the solar cells always agains the sun.
My problem is that once i have calculated the actual today's value i.e. numbers of days since 01/01/1970 i must add the constant Julian value, as in the code below, the hour value since midnight and subtract the time zone value.
Unfortunately for some reasons i am unable to understand the system doesn't adds up the values and for that reason i cant calculate the sun's azimuth and elevation for each block of 6 minutes of the day.
I did the same code in C# using VB.Net and it works like a charm as well as it does in Excel.
Anyone can help to understand where is my mistake? Because certainly there is at least one!!!
Thank you in advance for any suggestion.
Claudio

#define UNIX_BASE 25569UL // since 01/01/1970
#define EPOCH_BASE 36526UL // since 01/01/1900
#define ASTRON_BASE_VALUE 2415018.5 // number of days since 03/02/6512 12:00:00 BC
// for test only
int tYear, tMonth, tDay, tHour, tMin;
//
double SolarArray[6];
static const uint8_t monthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0
void setup() {
// setting up the system
// for test only
tYear = 2019, tMonth = 1, tDay = 12, tHour = 0, tMin = 0;
//
}

void loop()
{
    // uncomment next block of lines for real use
    /* 
    RTC.readTime();
    serialPrintTime();
    JToday = JulianDate(RTC.yyyy, RTC.mm, RTC.dd, RTC.h, RTC.m);
    Serial.print("Julian number of days = ");
    Serial.println(JToday);
    Calculate();
    if (SolarArray[3] > 0) {
        driveServos();
    }
    delay(60000); // one full minute 
    */
    // test procedure begins
    testSolarTracker();
    if (SolarArray[3] > 0) {
        driveServos(SolarArray[3], SolarArray[4]);
    }
    delay(3000); // three seconds
    // end test
}

double testSolarTracker()
{
    tMin += 6;
    if (tMin == 60) {
        tMin = 0;
        tHour += 1;
        if (tHour == 24) {
            tHour = 0;
        }
    }
    testPrintTime();
    double my_JToday = JulianDate(tYear, tMonth, tDay);
    Serial.print("Julian number of days = ");
    Serial.println(my_JToday);
    double my_JHour = (tHour * 60 + tMin) * 0.0006944 - TimeZone / 24;
    Serial.print("Julian number of hours = ");
    Serial.println(my_JHour);
    Calculate(my_JToday, my_JHour);
}

double JulianDate(int jyear, int jmonth, int jday)
{
    long yearDays = EPOCH_BASE;
    for (int myear = 2001; myear <= jyear; myear++) {
        if (myear % 4 == 0) {
            yearDays += 366;
        } else {
            yearDays += 365;
        }
    }
    for (int mmonth = 1; mmonth < jmonth; mmonth++) {
        yearDays += monthDays[mmonth - 1];
        if ((jyear % 4 == 0) && (mmonth == 1)) {
            yearDays += 1;
        }
        Serial.println(yearDays);
    }
    yearDays += jday;
    Serial.print("Total Julian Days = ");
    Serial.println(yearDays);
    return yearDays;
}

void Calculate(double this_JToday, double this_JHour)
{
    long D2 = this_JToday;
    double E2 = this_JHour;
    Serial.print("Calculate D2 + E2 = ");
    Serial.println(this_JToday + this_JHour);
    double F2 = D2 + E2 + ASTRON_BASE_VALUE;
    Serial.print("Calculate F2 = ");
    Serial.println(F2);
    double G2 = (F2 - 2451545L) / 36525L;
    Serial.print("Calculate G2 = ");
    Serial.println(G2);
// other calcs
}

Here is the printout:

[Info] Opened the serial port - COM8
YES Clock is running
RTC Time = 08:49:27, Date (D/M/Y) = 15/7/2019
RTC Status 2
stYear = 2019
tm.Year = 2019

Test Time = 00:06, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.00
Julian number of hours = 0.00
Calculate D2 + E2 = 43477.00
Calculate F2 = 2458495.50
Calculate G2 = 0.19

Test Time = 00:12, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.00
Julian number of hours = 0.01
Calculate D2 + E2 = 43477.01
Calculate F2 = 2458495.50
Calculate G2 = 0.19

Test Time = 00:18, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.00
Julian number of hours = 0.01
Calculate D2 + E2 = 43477.01
Calculate F2 = 2458495.50
Calculate G2 = 0.19

Test Time = 00:24, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.00
Julian number of hours = 0.02
Calculate D2 + E2 = 43477.01
Calculate F2 = 2458495.50
Calculate G2 = 0.19

Test Time = 00:30, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.00
Julian number of hours = 0.02
Calculate D2 + E2 = 43477.02
Calculate F2 = 2458495.50
Calculate G2 = 0.19

Test Time = 00:36, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.00
Julian number of hours = 0.02
Calculate D2 + E2 = 43477.02
Calculate F2 = 2458495.50
Calculate G2 = 0.19

as you can see the value of F" is the Astronomical base value, G" that uses the same valu as "long" it works fine.
I tried :

double F2 = D2 + E2 + 2451545L;

Test Time = 00:24, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.00
Julian number of hours = 0.02
Calculate D2 + E2 = 43477.01
Calculate F2 = 2495022.00
Calculate G2 = 1.19

F2 has been added up but no decimals, and this is as bad as before.

Thank you in advance for any suggestion.
Claudio

Thank you for your quick suggestion, but no changes, still not solved:
Test Time = 00:06, Date (D/M/Y) = 12/1/2019
Total Julian Days = 43477
Julian number of days = 43477.0000
Julian number of hours = 0.0042
Calculate D2 + E2 = 43477.0039
Calculate F2 = 2495022.5000
Calculate G2 = 1.1903

and what it is funny i found another strange behavior :
Julian number of days = 43477.0000
Julian number of hours = 0.0042
Calculate D2 + E2 = 43477.0039 !!!! instead of 46477.0042 !!!

On the Nano (as on all Arduino eight bitters) double and float are the same thing.

Yes, I understand that, but it doesn't explain the math results.... :slight_smile:

Claudio_tls:
Yes, I understand that, but it doesn't explain the math results.... :slight_smile:

Because all floating point numbers in the Arduino family are 4-byte values, the best you can hope for are 6 to 7 digits of precision. Any digits after that is the compiler's best guess at the number. Still, the values:

Calculate D2 + E2 = 43477.0039 !!!! instead of 46477.0042 !!!

are odd since I would expect those significant digits to be the most significant part of the number.

econjack:
Because all floating point numbers in the Arduino family are 4-byte values, the best you can hope for are 6 to 7 digits of precision.

Is it the precision or accuracy?

a solar follower to orientate the solar cells always agains the sun.

The hard work of accurately calculating the sun's altitude and azimuth has been done using only single precision Arduino floats. Works anywhere on Earth at any time of day.

Convenient library here: GitHub - KenWillmott/SolarPosition: Arduino Library to calculate the position of the sun relative to geographic coordinates

Dr. Brooks program uses equations are from the book, "Jean Meeus, Astronomical Algorithms, Willmann-Bell, Inc., Richmond, VA". A very important aspect is that straightforward floating point calculations for those equations are likely to introduce significant errors with the 32 bit float type used on the Arduino. So a special method of splitting Julian days into an integer and fractional part is used to overcome that problem.

A 32-bit floating point number has a 24-bit mantissa (INCLUDING the sign bit). That means it can only capture numbers between 0 and -8,388,608 and +8,388.607, which can then be raised to a binary exponent of +/-127. Note that there are only SEVEN digits there. It CANNOT accurately represent ANY number having more than 7 digits of precision (technically, about 6-1/2 decimal digits of precision). Many of your intermediate results are WELL outside this precision, hence the results are wrong.

You may well have better luck using long integers, rather than floats. Otherwise, you need to use a processor (Due, and other NON-AVR processors) that support 64-bit doubles. Your only other option is to re-work your calculations, keeping the above in mind, so you never exceed the limits of float precision.

Regards,
Ray L.

jremington:
The hard work of accurately calculating the sun's altitude and azimuth has been done using only single precision Arduino floats. Works anywhere on Earth at any time of day.

Convenient library here: GitHub - KenWillmott/SolarPosition: Arduino Library to calculate the position of the sun relative to geographic coordinates

Thank you Jremington and RayLivingston, your answers are really what i was looking for, first the reason of my mistakes and second where the solution could be found.

In any case i have deeply enjoyed the effort to understanding the NOAA solar calculation, and i am now ready to explore the Ken Wilmott solar Library.
Very pleased, really thank you again.