Real/LONG values

On 17/01/2014 at 17:51, xxxxxxxx wrote:
Hi Remo,
thanks for taking the time to have a look. I'm aware of the rounding that happens with LONGs, and have been using it to remove decimal values for some time. I perhaps should be using the Round() instead of the LONG and copying it back, but either way it still works (or certainly has in other areas previously).
The problem I'm getting though, is that inputting the same numbers (both decimal and rounded) into a calculator gives me the correct result both times, but in code running in a plugin it does not.
I'll need to have a read of the floating point link you've provided Remo just to check over. But there's clearly something I'm not getting in the printed values. I chose 25 frames per second because each frame pops up at 4ms interval. 4 goes into 100 without any leftovers. Why then does millisecond end up at 15, as 4 does not go into 15 (not without decimal places anyway). The Get() call on the BaseTime is returning 1.16, but after the leftover is worked out and turned into a ms and passed to milliseconds (which for this purpose I thought was 0.16*100), I'm left with 15, not 16. It's a very simple mathematical calculation that is either hampered by poor internal computer calculations, or (and quite possibly!) there's something about the decimal value Reals I'm still not seeing/grasping. But to me the math is simple  the Get() value in this case can only ever return something to two decimal places at most (due to the nature of 25fps), yet this does not seem to be the case here.
Before I go any further, how can I get and print a float value? Or is this what a Real is and what FormatNumber() do!?
WP.

On 19/01/2014 at 08:29, xxxxxxxx wrote:
Just an observation.
This is not the same
time.SetDenominator(framerate); time.SetNumerator(currentframe);
as this
time = BaseTime(currentframe,framerate);

On 19/01/2014 at 18:00, xxxxxxxx wrote:
Interesting, though I wonder why it wouldn't be the same? I just changed the code to try that Remo out of interest, but I ended up with the same incorrect printed results.
One other thing I also tried was multiplying the leftover value by 1000 instead of 100 to see if that could shed any light on other hidden decimal places. The value it gave me was 159 (0.16*1000 = 159..??). So there's something fishy to me going on behind the scenes that I can't see. The leftover value should have been 0.159 if that be the case, but neither the FormatNumber() or RealToString() printed that value (even though it's wrong).
I'm going to have a bit of a further play with the *1000 way. Might be able make some sort of fail safe out of it. But nevertheless, I'm quite struck at observing such a seemingly simple calculation end up like it does. I would have thought in this day and age computers could handle this quite comfortably...
WP.

On 19/01/2014 at 19:57, xxxxxxxx wrote:
OK, I'm genuinely quite stumped on this. When I change the millisecond to a float value, it works as expected (the printed numbers are then correct).
Is someone from Maxon able to provide some insight into why the Real values are not kosher in the above/below example?
Put the following functions into a plugin and run them one after the other. The printed millisecond values (see last CODE block) are just not the same. Please disregard all the comments and doublelining etc, I've done a bit of fiddling and tweaking and have had enough of doing that with this one =), so things might not sit correctly  but it should serve it's purpose here.
void TestPrints_Real(void) { Real framerate; //LONG framecount; Real currentframe; Real sec = 0.0; LONG hours; LONG minutes; Real seconds; Real milliseconds; Real leftover = 0.0; BaseTime time; currentframe = 29.0; framerate = 25.0; time = BaseTime(currentframe,framerate); //time.SetDenominator(framerate); //time.SetNumerator(currentframe); sec = time.Get(); LONG sec_L = sec; //Real t_sec = sec*1000.0; //GePrint("t_sec = " + RealToString(t_sec)); //String hub_bub = FormatNumber(t_sec,FORMAT_REAL,NULL,FALSE); //GePrint("String t_sec = " + hub_bub); ////LONG sec_t = t_sec; ////Real sdg = sec_t; ////Real bleh = sdg/1000; ////sec = bleh; hours = sec_L / 3600.0;//implicit conversion to integer if (hours < 0){ hours = 0; } minutes = (sec_L  (hours * 60.0)) / 60.0;//implicit conversion to integer if (minutes < 0){ minutes = 0; } seconds = (sec_L  (hours * 3600.0)  (minutes * 60.0));//implicit conversion to integer if (seconds < 0){ seconds = 0; } Real blip = secsec_L; GePrint("blip = " + RealToString(blip)); leftover = blip;//(sec  ((hours * 3600.0) + (minutes * 60.0) + seconds)); milliseconds = leftover * 100000.0; //implicit conversion to integer if (milliseconds < 0){ milliseconds = 0; } GePrint("currentframe = " + RealToString(currentframe)); GePrint("sec = " + RealToString(sec)); String str = FormatNumber(sec, FORMAT_REAL, NULL, FALSE); GePrint("String SEC = " + str); GePrint("hours = " + LongToString(hours)); GePrint("minutes = " + LongToString(minutes)); GePrint("seconds = " + LongToString(seconds)); GePrint("milliseconds = " + LongToString(milliseconds)); String str2 = FormatNumber(milliseconds, FORMAT_REAL, NULL, FALSE); GePrint("String milliseconds = " + str2); GePrint("leftover = " + RealToString(leftover)); String str3 = FormatNumber(leftover,FORMAT_REAL,FALSE,FALSE); GePrint("String leftover = " + str3); //Real t = leftover*100; //milliseconds = Round(t); //GePrint("ROUNDED milliseconds = " + LongToString(milliseconds)); // String str4 = FormatNumber(milliseconds, FORMAT_REAL, NULL, FALSE); GePrint("ROUNDED String milliseconds = " + str4); //Real t_ms = 100/framerate; //LONG t_ms_L = t_ms*10; //Real t_ms_L_R = t_ms_L; //Real divided = milliseconds/t_ms_L_R; //Real divided_refresh = divided*10; ////LONG divi_1 = divided*10; //if(divided != t_ms) //{ // GePrint(" "); // GePrint("Failsafe reached:"); // GePrint("t_ms = " + RealToString(t_ms)); // GePrint("t_ms_L = " + LongToString(t_ms_L)); // GePrint("t_ms_L_R = " + RealToString(t_ms_L_R)); // GePrint("divided = " + RealToString(divided)); // GePrint("divided_refresh = " + RealToString(divided_refresh)); // //GePrint("Divided milliseconds = " + RealToString(divided)); //} //Real next_idea = sec*1000/framerate; //GePrint(" "); //GePrint("next_idea = " + RealToString(next_idea)); GePrint(" "); } void TestPrints_Float(void) { Real framerate; //LONG framecount; Real currentframe; Real sec = 0.0; LONG hours; LONG minutes; Real seconds; float milliseconds; Real leftover = 0.0; BaseTime time; currentframe = 29.0; framerate = 25.0; time = BaseTime(currentframe,framerate); //time.SetDenominator(framerate); //time.SetNumerator(currentframe); sec = time.Get(); LONG sec_L = sec; //Real t_sec = sec*1000.0; //GePrint("t_sec = " + RealToString(t_sec)); //String hub_bub = FormatNumber(t_sec,FORMAT_REAL,NULL,FALSE); //GePrint("String t_sec = " + hub_bub); ////LONG sec_t = t_sec; ////Real sdg = sec_t; ////Real bleh = sdg/1000; ////sec = bleh; hours = sec_L / 3600.0;//implicit conversion to integer if (hours < 0){ hours = 0; } minutes = (sec_L  (hours * 60.0)) / 60.0;//implicit conversion to integer if (minutes < 0){ minutes = 0; } seconds = (sec_L  (hours * 3600.0)  (minutes * 60.0));//implicit conversion to integer if (seconds < 0){ seconds = 0; } Real blip = secsec_L; GePrint("blip = " + RealToString(blip)); leftover = blip;//(sec  ((hours * 3600.0) + (minutes * 60.0) + seconds)); milliseconds = leftover * 100000.0; //implicit conversion to integer if (milliseconds < 0){ milliseconds = 0; } GePrint("currentframe = " + RealToString(currentframe)); GePrint("sec = " + RealToString(sec)); String str = FormatNumber(sec, FORMAT_REAL, NULL, FALSE); GePrint("String SEC = " + str); GePrint("hours = " + LongToString(hours)); GePrint("minutes = " + LongToString(minutes)); GePrint("seconds = " + LongToString(seconds)); GePrint("milliseconds = " + LongToString(milliseconds)); String str2 = FormatNumber(milliseconds, FORMAT_REAL, NULL, FALSE); GePrint("String milliseconds = " + str2); GePrint("leftover = " + RealToString(leftover)); String str3 = FormatNumber(leftover,FORMAT_REAL,FALSE,FALSE); GePrint("String leftover = " + str3); //Real t = leftover*100; //milliseconds = Round(t); //GePrint("ROUNDED milliseconds = " + LongToString(milliseconds)); // String str4 = FormatNumber(milliseconds, FORMAT_REAL, NULL, FALSE); GePrint("ROUNDED String milliseconds = " + str4); //Real t_ms = 100/framerate; //LONG t_ms_L = t_ms*10; //Real t_ms_L_R = t_ms_L; //Real divided = milliseconds/t_ms_L_R; //Real divided_refresh = divided*10; ////LONG divi_1 = divided*10; //if(divided != t_ms) //{ // GePrint(" "); // GePrint("Failsafe reached:"); // GePrint("t_ms = " + RealToString(t_ms)); // GePrint("t_ms_L = " + LongToString(t_ms_L)); // GePrint("t_ms_L_R = " + RealToString(t_ms_L_R)); // GePrint("divided = " + RealToString(divided)); // GePrint("divided_refresh = " + RealToString(divided_refresh)); // //GePrint("Divided milliseconds = " + RealToString(divided)); //} //Real next_idea = sec*1000/framerate; //GePrint(" "); //GePrint("next_idea = " + RealToString(next_idea)); GePrint(" "); }
and place these somewhere (in a button command or something) :
TestPrints_Real(); TestPrints_Float();
My printed values are:
// REAL blip = 0.16 currentframe = 29 sec = 1.16 String SEC = 1.16 hours = 0 minutes = 0 seconds = 1 milliseconds = 15999 > INCORRECT String milliseconds = 16000 leftover = 0.16 String leftover = 0.16 // FLOAT blip = 0.16 currentframe = 29 sec = 1.16 String SEC = 1.16 hours = 0 minutes = 0 seconds = 1 milliseconds = 16000 > CORRECT String milliseconds = 16000 leftover = 0.16 String leftover = 0.16
Hoping it's just me, but worry it's something far more fundamental. Regards,
WP.

On 20/01/2014 at 01:31, xxxxxxxx wrote:
Be aware that 'Real' is not the same as 'float'. In any recent version of Cinema, Real is defined as LReal, and that in turn is a double. The precision is therefore different and that explains the differences you see.
Try 'double milliseconds' as opposed to 'float milliseconds' and you should see the same results as for Real.

On 20/01/2014 at 05:50, xxxxxxxx wrote:
As spedler says. And, despite the extra work, I am so glad that Maxon has decided to change its base types by bitsize rather than let the particular system make the determination. There is an entire thread here somewhere about this issue (generic int that changes bitsize based on system and bitsize versus int32, etc.).

On 20/01/2014 at 11:18, xxxxxxxx wrote:
Hi,
Real is actually a double (8bytes) and is more precise as float (SReal).
What you are doing all the time is to implicitly converting floating points values to integer values and then back again, usually this is bad thing.
Please also note that for longer calculation using floating points errors are accumulations pretty fast.
If you are dealing with floating points then 0.15999999 is "the same" as 0.1600000 only with a small error that can happens all the time.
This is why C4D uses BaseTime and not simple Real for time in seconds.Remo

On 20/01/2014 at 12:13, xxxxxxxx wrote:
Using new version of my C4D++ :)
I was able to make this function that show and probably solves you problem.
As you can see the value is "159.999999999999910" this is almost 160.
As it seems such lines are "(sec  (hours * 3600.0)  (minutes * 60.0)  seconds)" is probably source of extra calculation error.
Any way this are usual problems with floating points arithmetics.The function below is still not really correct but better.
{ //inline LONG LRound(Real x) { return Floor(x + 0.5); } auto LRound = [](Real x) { return Floor(x + 0.5); }; Real currentframe = 29.0; for(currentframe = 10.0; currentframe<40.0; currentframe+=1) { Real framerate = 25.0; BaseTime time(currentframe,framerate); const Real sec = time.Get(); Real hours = Floor(sec / 3600.0); //Floor() Calculates the largest previous integer number. Real minutes = Floor(sec / 60.0)  (hours * 60.0); Real seconds = Floor(sec  (hours * 3600.0)  (minutes * 60.0)); Real leftover = (sec  (hours * 3600.0)  (minutes * 60.0)  seconds); Real milliseconds = (leftover * 1000.0); //Ceil() Calculates the smallest following integer number. LONG milliseconds_l = LRound(milliseconds); GePrint("currentframe = " + RealToString(currentframe,1,6)); GePrint("sec = " + RealToString(sec,1,6)); GePrint("hours = " + LongToString(hours)); GePrint("minutes = " + LongToString(minutes)); GePrint("seconds = " + LongToString(seconds)); GePrint("milliseconds = " + LongToString(milliseconds_l) +" "+ RealToString(milliseconds,1,15)); GePrint("leftover = " + RealToString(leftover,1,6)); GePrint(""); } }
Remo

On 22/01/2014 at 18:51, xxxxxxxx wrote:
Thanks for the input all. I have a bit to read up on about these floats/doubles etc by the looks of it!
From a noncoders perspective, I'm quite struck to see potential numerical discrepancies like these in such a simple example. I've since done some playing about with LONGs, Reals, ints, floats etc and have found some strange behaviour in processing numbers at times. The kind of thing where 60 != 60.0!! I'm a little surprised that modern day architecture can't handle these things, but nevertheless workarounds are there.
Thanks again,
WP.

On 22/01/2014 at 22:42, xxxxxxxx wrote:
With integer types, as long as there is no overflow in value, they will always be 100% accurate.
Floating point (real) numbers are represented using an IEEE numerical formatting of bits. Since you can only have so many decimal places represented (socalled 'significant digits'), it is mathematically impossible to avoid rounding errors and, in subtraction, cancellation errors. All one can do is use algorithms and methodologies that minimize direct and accumulative errors. No amount of architecture can avoid these potential numerical discrepancies.

On 23/01/2014 at 05:15, xxxxxxxx wrote:
The kind of thing where 60 != 60.0!!
Yes for floating point number you need to assume this all the time.> I'm a little surprised that modern day architecture can't handle these things, but nevertheless workarounds are there.
This has nothing to do with the architecture but more with the mathematics.
Some real numbers need infinite memory so it is just impossible to represent them as floatin point numbers.
Of course there are some other ways to represent them that have other advantages and disadvantages, but there is just no hardware support for them.Some times one need to use much slower way for calculation such as "arbitraryprecisionarithmetic".
Remo