Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Float Increment Issue.

Discussion in 'Scripting' started by Bakudan, Apr 4, 2015.

  1. Bakudan

    Bakudan

    Joined:
    Mar 29, 2015
    Posts:
    16
    Hello there !
    I got i really weird issue

    I have a turbo toogle to speed up my test ingame which is :
    Code (CSharp):
    1.         if (turboMode == true){
    2.                 delay = 0.005f;
    3.             }else{
    4.                 delay = 0.1f;
    5.             }
    And a condition like this :
    Code (CSharp):
    1. dps_animation_timer += delay;         //(Mathf.Round (delay*10))/10;
    2.             Debug.Log (dps_animation_timer);
    3.             //Debug.Log ((Mathf.Round (delay*10))/10);
    4.             if (dps_animation_timer % (delay*2) == 0)
    5. {
    6. do something
    7. }
    The problem is the condition is never reached because when i check the debug log, i got this

    " dps_animation_timer = 0.1
    dps_animation_timer = 0.199999
    dps_animation_timer = 0.299998
    dps_animation_timer = 0.399997 ..."

    and so on, i can't figure why incrementing +0.1f to a float variable give me +0.099999 instead...
    I try with some math.round trick but same result...
    Anybody have a solution ?

    (I should specifie that the incrementation is running in a While loop inside a Coroutine,
    with : yield return new WaitForSeconds (delay);
    but it shouldn't matter).

    Thanks^^
     
    Last edited: Apr 4, 2015
  2. PGJ

    PGJ

    Joined:
    Jan 21, 2014
    Posts:
    897
    Binary fractions are notoriously prone to rounding errors. Google it if you want more in-depth explanation.

    The short answer is; Instead of using "==", use Mathf.Approximately().
     
  3. Bakudan

    Bakudan

    Joined:
    Mar 29, 2015
    Posts:
    16
    Ho, i didn't know about this.
    Thanks for the tip but i just tried :
    Code (CSharp):
    1.         Debug.Log (Mathf.Approximately((dps_animation_timer % (delay*2)),0f));
    2.  
    3.             if ( Mathf.Approximately((dps_animation_timer % (delay*2)),0f))
    4.             {
    5.  
    6.                 particle1.enableEmission = true;
    7.                 particle2.enableEmission = false;
    8.             }else{
    9.                                  particle2.enableEmission = true;
    10.                 particle1.enableEmission = false;
    11. }
    And i got "true" only the first few time then its always "false", like if the approximation wasen't right :S
    Did i do something wrong ?
     
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Mathf.Approximately is only for very tiny values (epsilon), and shouldn't be used as general comparison function. You can make your own function to compare similar values instead.

    --Eric
     
  5. Bakudan

    Bakudan

    Joined:
    Mar 29, 2015
    Posts:
    16
    Hum i really don't see how i can compare a Modulo float input to be true with those 0.00001 float error since i need a perfect number.

    And i absolutely need float number because it has to be looping several time in a second :S.
    I mean event adding 0.1f to my float variable give me sometimes 0.100001 in addition, there is no easy way to get rid of this ?


    EDIT : I almost made it with this :
    Code (CSharp):
    1. if ( Mathf.Round (dps_animation_timer*10000f) % Mathf.Round ((delay*2)*10000f) == 0)
    So i get value with no weird float error b ti doubt its the best solution no ?
     
    Last edited: Apr 4, 2015
  6. PGJ

    PGJ

    Joined:
    Jan 21, 2014
    Posts:
    897
    Can't you just multiply your values with a 1000 to start with? And scale the result down if you really need to.
     
  7. Bakudan

    Bakudan

    Joined:
    Mar 29, 2015
    Posts:
    16
    Absolutly, it also work with just 1000, and since i heard that Coroutine looping delay is bound to the game current FPS, i guess i don't need to have a lower value than "delay = 0.01f".
     
  8. Evil Square

    Evil Square

    Joined:
    Mar 21, 2015
    Posts:
    18
    I've just encountered the same problem in my game.

    I'm using UnityScript instead of C#, but I still have the same result:

    Code (JavaScript):
    1. if (preSec != curSec)
    2. {
    3.     preSec = curSec;
    4.     speedTextValue = speedTextValue + 0.1;
    5.     speedText.text = speedTextValue + " m/s";
    6. }
    The code within the if statement runs every second in the FixedUpdate function. Every second, 0.1 is added to the speedTextValue (which starts off at 0.0). So you would expect it to go from 0.7 to 0.8, but instead it goes from 0.7 to 0.8000001, then to 0.9000001, then to 1.

    I have also tried playing with Mathf.Round, but that only rounds the float to the nearest integer. I want it to round to the nearest tenth instead, still leaving it as a float.

    Now, normally this wouldn't really concern people since it's such a small error that it usually goes unnoticed, but I am setting a text element in the scene to equal the value I want rounded, so it won't do in my case, as it looks kinda weird to go from 0.7 to 0.8000001. ;)

    Should I try doing the multiplication by 1000 trickery or is there something that can be done to tidy up the displayed text?

    Any help would be appreciated!

    -Chris
     
  9. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    You can use System.Math.Round instead of Mathf.Round to round to a specific number of decimal places. That might make both of your lives easier.

    Code (csharp):
    1. if (System.Math.Round(dps_animation_timer % (delay*2), 2) == 0)
    Code (csharp):
    1. speedTextValue = System.Math.Round(speedTextValue + 0.1, 2);
     
    Evil Square likes this.
  10. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    It would, as mentioned above, be a lot better if you just used ints to begin with, then you don't have to round anything and can bypass the entire issue. In other words, have your speedTextValue go from 7 to 8 instead of .7 to .8. You can just divide by 10 when you need the "real" value.

    --Eric
     
    Evil Square likes this.
  11. Evil Square

    Evil Square

    Joined:
    Mar 21, 2015
    Posts:
    18
    @makeshiftwings Thanks a bunch! I didn't know about System.Math. All the documentation had was Mathf.

    @Eric5h5 Yeah, that could work too.

    I've been giving myself a headache for a few hours over this one while there was a very simple solution right under my nose. :p

    Thanks all!

    -Chris
     
  12. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
  13. RabenFutter

    RabenFutter

    Joined:
    Dec 30, 2012
    Posts:
    38
    This seems like a default float point error.

    I would just re write it completely without an modulus. It looks like you want to play an animation with a specific frequency and change it based on an input.
    Code (CSharp):
    1.  
    2. float currentTimer = 0f;
    3. float frequency = 1f; //in seconds
    4.  
    5. void Update(){
    6.     float mod = 1f;
    7.     if(intput == turbo){
    8.         mod = 2f;
    9.     }
    10.     else if(input == pause){
    11.          mod = 0;
    12.     }
    13.     currentTimer += mod*Time.deltaTime;
    14.     if(currentTimer >=  frequency){
    15.         do something;
    16.          currentTimer -= frequency;
    17.     }
    18. }
     
    Last edited: Apr 14, 2015