Double/float conversion in time-based calculation won't update

This is strange or I’m missing something. The code is working, the calculations are right (about sun position using current date/time and latitude and longitude values) the relevant variables are updating quickly (hopefully every few milliseconds) in the functions (I can see this using the console) but in the draw() loop the final calculation (and even the initial timer) is just stalled.
Any ideas? This is driving me nuts.

Edit: This should behave like another script I just did in javascript at https://bit.ly/2YQS33z --just two numbers updating every few milliseconds. Same calculation. JS source available with view source, using an old js library -suncalc.js --the calculations are somewhat equivalent, the main problem is the values won’t change quickly in the draw loop, something related to the double/float conversion of some values, as @William3954 pointed out. The problem here is that floats lost floating point precision (something crucial here) but in some cases I can’t pass double values to functions. Thanks a lot for any help!


import java.util.*; 

double dayMs = 1000 * 60 * 60 * 24;
float J1970 = 2440588;
float J2000 = 2451545;
float lat = 4.596674;
float lng = -74.06525;
DoubleDict sp = new DoubleDict();


void setup() {
  frameRate(300);
  size(400,300);
  
  fill(250); color (250);
}


void draw(){
    background(0);
    
    sp = sunPosition (lat, lng);
    
    float alt = degrees((float) sp.get("altitude"));
    float azt = degrees((float) sp.get("azimuth")+3.141516);
    
  //  println("alt", alt);
  //  println("azt", azt);
    
    text(nf((float) toDays(),2,10),10,20);
    text(nf(alt,2,10),10,50);
    text(nf(azt,3,10),10,80);
    
}


// shortcuts for easier to read formulas
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas

double toJulian() { return System.currentTimeMillis() / dayMs - 0.5 + J1970; }
// float fromJulian(float j)  { return new Date((j + 0.5 - J1970) * dayMs); }
double toDays()   { return toJulian() - J2000; }


// general calculations for position
float rad  = PI / 180;
float e = rad * 23.4397; // obliquity of the Earth

float rightAscension(float l, float b) { return atan2(sin(l) * cos(e) - tan(b) * sin(e), cos(l));  }
float declination(float l, float b)    { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }

float azimuth(float H, float phi, float dec)  { return atan2(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
float altitude(float H, float phi, float dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }

float siderealTime(double d, float lw) { return rad * (280.16 + 360.9856235 * (float) d) - lw; }

float astroRefraction(float h) {
    if (h < 0) // the following formula works for positive altitudes only.
        h = 0; // if h = -0.08901179 a div/0 would occur.

    // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
    // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
    return 0.0002967 / tan(h + 0.00312536 / (h + 0.08901179));
}

// general sun calculations

float solarMeanAnomaly(double d) { return rad * (357.5291 + 0.98560028 * (float) d); }

float eclipticLongitude(float M) {

    float C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)); // equation of center
    float P = rad * 102.9372; // perihelion of the Earth

    return M + C + P + PI;
}

FloatDict sunCoords(double d) {
    
  FloatDict suncoords = new FloatDict();
  
  float M = solarMeanAnomaly(d);
  float L = eclipticLongitude(M);
  
  suncoords.add("dec", declination(L, 0));
  suncoords.add("ra", rightAscension(L, 0));
  
  return suncoords;

}

// calculates sun position for a given date and latitude/longitude

DoubleDict sunPosition (float lat, float lng) {

  DoubleDict sunpos = new DoubleDict(); 
    
  float lw  = rad * -lng;
  float  phi = rad * lat;
  double  d   = toDays();
  
  println(d);
  
  FloatDict  c  = sunCoords(d);
  float  H  = siderealTime(d, lw) - c.get("ra");
  
  sunpos.set("azimuth", azimuth(H, phi, c.get("dec")));
  sunpos.set("altitude", altitude(H, phi, c.get("dec")));
  
  // conversion error
  println("double: " + (double) toDays());
  println("float: "  + (float) toDays());
  println("float to double" + nf((float) toDays(), 2, 10));

  return sunpos;

}
1 Like

you never change the latitude or longitude and days is just drawn from the current day. increment lat and lng in your draw loop and you will see the values change. or at least that’s my first thought after a quick look.

Thanks for your message! The lat/long is constant. What changes is the system Time variable (and from that changes the calculation of the position of the sun in that location) inside the sunPosition function.

You need to provide the js code. Taking a wild guess, you are not using the proper units in your trig functions, could be that you are using msecs where you suppose to use secs (a factor of a thousand off) or some other detail that it will be obvious after comparing both codes. In addition, you should get some “test” numbers from your js code and trace them in your java code.

Kf

1 Like

You convert a double into a float and then back to a double which is displayed.
I added this code prior to “return sunpos;” and you can see how the precision is lost. The info you are displaying in the console is “d”, essentially the first double. Then that snippet gets cast into a float. Finally the toDays() function does calcs on it and returns it as a double. The precision is only going to be as precise as the float.
Another way of saying it would be take x=4.5, cast X to and integer and you get 5 and divide by 2. Answer is 2.5. Next, x=4.6, cast X to and integer and you get 5 and divide by 2. Answer is 2.5. and so on. There won’t be a change until the float reaches the next unit. I believe there is also some precision issues with processing and doubles as well, if I remember correctly, a double that is not changing can return different float values or something of the like.
https://forum.processing.org/two/discussion/16374/adding-0-1-with-double-and-float-is-not-accurate

println("double: " + (double) toDays());
println("float: "  + (float) toDays());
println("float to double" + nf((float) toDays(), 2, 10));

Console log–
double: 7135.0773065742105
float: 7135.077
float to double: 7135.0771484375

3 Likes

The javascript code is on that link with ‘view source’, i’m using and old sun calculation javascript library, suncalc.js but actually the port is giving perfect results --is more a matter of the refreshing of the data in the draw loop. The values won’t change quickly in every frame.

Thnaks a lot @William3954 --really helpful. The initial code used floats for every function but then I realized I lost floating point precision (something that I need in this case). The problem is that doubles won’t be accepted by functions in some cases --so I had to do the conversion to floats beforehand.

Any ideas about how manage doubles and its presentation in the draw loop?

One idea could be to using an external library. Instead of using Processing’s functions for your math, use import java.lang.Math; and their functions directly as they will accept doubles.
https://www.geeksforgeeks.org/java-math-sin-method-examples/

BigDecimal might also interest you:
https://www.tutorialspoint.com/java/math/java_math_bigdecimal

3 Likes

Using Java Math.functions and DoubleDict solved it. Thanks a lot!

4 Likes