Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Real/LONG values

    SDK Help
    0
    13
    1.1k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      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);  
          
      
      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        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 never-the-less, 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.

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          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 double-lining 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 = sec-sec_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 = sec-sec_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.

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            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.

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              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 bit-size rather than let the particular system make the determination.  There is an entire thread here somewhere about this issue (generic int that changes bit-size based on system and bit-size versus int32, etc.).

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 20/01/2014 at 11:18, xxxxxxxx wrote:

                Hi,

                Real is actually a double (8-bytes) 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

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  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

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    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 non-coders 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 never-the-less workarounds are there.

                    Thanks again,

                    WP.

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      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 (so-called '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.

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        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 never-the-less 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 "arbitrary-precision-arithmetic".

                        Remo

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post