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

Adding 0f to a float adds 0.01f

Discussion in 'Scripting' started by abertrand, Feb 2, 2016.

  1. abertrand

    abertrand

    Joined:
    Dec 7, 2015
    Posts:
    29
    Hi all,

    Something quite puzzling is happening that I can't explain. I am writing a script to make a GameObject turn in a circle motion at a specific velocity. The summary of what I have done is:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class ObjectTurner : MonoBehaviour {
    6.  
    7.     public float maxAngle = 350f;
    8.     public float angularVelocity = 0.5f;
    9.     public float angularAcceleration = 0.0000000000000f;
    10.  
    11.     IEnumerator Turn(){
    12.  
    13.         //Start our position at 0 degree
    14.         float degreeTurn = 0f;
    15.         transform = CalculatePos(degreeTurn, 3f, 3f);
    16.  
    17.         //Continue our coroutine until we've reached maxAngle
    18.         while (degreeTurn <= maxAngle) {
    19.  
    20.             //Our transform turns at a specific speed
    21.             degreeTurn += angularVelocity;
    22.  
    23.             //That speed accelerates
    24.             handAngularVelocity += angularAcceleration;
    25.  
    26.             transform = CalculatePos(degreeTurn, 3f, 3f);
    27.  
    28.             yield return null;
    29.         }
    30.     }
    31.  
    32.  
    33.     private Vector3 CalculatePos(float degree, float height, float radius){
    34.  
    35.         float rad = degree * Mathf.Deg2Rad;
    36.  
    37.         float xPos = Mathf.Cos (rad) * radius;
    38.         float zPos = Mathf.Sin (rad) * radius;
    39.  
    40.         Vector3 resultPos = new Vector3 (xPos, height, zPos);
    41.  
    42.         return resultPos;
    43.     }
    44. }
    My issue is that it doesn't matter what value I put for angularAcceleration, my velocity always gets increased by 0.01f (including if I put 0.0000000000000f like in my code above) Only if I comment out angularVelocity += angularAcceleration does my angularVelocity remain constant from frame to frame (i.e. at 0.5f like in my code).

    Very puzzling, can someone explain the magic? I am guessing it might have to do with float types maybe?

    Thank you!
     
  2. abertrand

    abertrand

    Joined:
    Dec 7, 2015
    Posts:
    29
    I solved my issue by making angularVelocity and angularAcceleration decimals; my hypothesis was correct, seems to have been a data type issue. Still puzzled as to what the issue was though...
     
  3. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    It's probably caused by floating point precision, yeah. Because of how they work, they can't store arbitrary decimals with infinite precision. I don't have a good link on hand, but if you want to know more, check out Wikipedia's article on floating point numbers.
     
    abertrand likes this.
  4. abertrand

    abertrand

    Joined:
    Dec 7, 2015
    Posts:
    29
    Thanks for the reply but I am not entirely sure it is a question of precision for relatively "extreme" numbers because if it was it would still understand the difference between 10 and 0.1 or 0 but no matter what number I put it would simply add 0.01
     
  5. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Seems odd. Are you checking the value in the debugger, or Debug.Log()? In my experience, Debug.Log() rounds the number to hell.
     
    LeftyRighty likes this.
  6. abertrand

    abertrand

    Joined:
    Dec 7, 2015
    Posts:
    29
    Yes I am checking the numbers with Debug.Log() but 1) when I use decimals type the numbers show correctly in Debug.Log and 2) it is obvious in my game that the acceleration is always 0.01f, no matter which number I enter (and again when I use decimals type the acceleration changes consistently with the numbers in my code). So I don't think it's an issue with Debug.Log, the numbers are indeed always 0.01f
     
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,201
    Adding 0f to a float should be safe. Printing floats should also always be correct - you're getting out the current representation of the float. There's probably some faulty assumption here. Do a full debug:

    Code (csharp):
    1. Debug.Log("Adding " + angularAcceleration + " to " + handAngularVelocity);
    2. handAngularVelocity += angularAcceleration;
    3. Debug.Log("Result: " + handAngularVelocity");
    That's not going to print "Adding 0 to n, result n + 0.01".
     
  8. abertrand

    abertrand

    Joined:
    Dec 7, 2015
    Posts:
    29
    Ok, I did what you just said and the result is the weirdest, screenshots to prove it:
    1. I start by declaring my variables. Notice that angularAcceleration is 0.02f
    2. I then copied your code
    3. What appears in Debug.Log? Our old 0.001f. Wtf?
    Again if I simply replace
    Code (CSharp):
    1. public float handAngularVelocity = 0.7f;
    2. public float handAngularAcceleration = 0.02f;
    with
    Code (CSharp):
    1. public decimal handAngularVelocity = 0.7m;
    2.  public decimal handAngularAcceleration = 0.02m;
    then it all behaves perfectly as expected.
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,201
    This is a MonoBehaviour, which means that if you have set a value in the inspector, that value will override whatever value you've written in the variable declaration. That's probably what's going on.

    If you don't want the value to be settable from the inspector (but still public), use the NonSerialized attribute:

    Code (csharp):
    1. [System.NonSerialized]
    2. public float handAngularVelocity = 0.7f;
    EDIT: replacing with decimal works because Unity can't (and doesn't attempt to) serialize decimals.
     
    abertrand and lordofduct like this.
  10. Immanuel-Scholz

    Immanuel-Scholz

    Joined:
    Jun 8, 2013
    Posts:
    221
    My first gut feeling when reading the code is, that you just changed the value in the code, but once had it set to 0.01f in the inspector.

    So you changed the default value that a new instance of ObjectTurner would have, but not the actual value of the existing instance.

    Happens to me all the time :(

    Edit: aaaaaand Baste beat me.. ^^
     
    abertrand and lordofduct like this.
  11. Deleted User

    Deleted User

    Guest

    I had to chime in here to test this myself and in Linqpad using Microsoft's Console.Write it is also showing issues with floating point precision using a float.



    However:
    Converting everything to a double, the precision seems to be spot on. I have also run into trouble using floats for math and, while I am a firm believer in only using the variables that you need, considering a double is overkill for a great deal of calculations, it seems that floats are an issue all around if your goal is math precision.



    Now:

    A pretty solid solution that I used in a game I'm currently programming is to perform ALL the math in a double or a decimal then cast as a float when you assign the TOTAL into the vector field. It will retain the precision that way. Like so (I won't relink the massive screenshot again but the math precision stays good as long as the math calculations are done while it's still a double:

    Code (CSharp):
    1. void Main()
    2. {
    3.     double handAngularAcceleration = 0.02d;
    4.     double handAngularVelocity = 0.7d;
    5.     for(int i = 0; i < 100; i++)
    6.     {
    7.         handAngularVelocity += handAngularAcceleration;
    8.         Console.Write("handAngularVelocity: " + (float)handAngularVelocity + "\n");
    9.     }
    10. }


    On an unrelated note, some people don't know that if I do this:



    It's because the compiler reads the 5 and 2 in the first example as integers and integers are always rounded DOWN to a whole number so even if you are assigning it as a double, the math is done assuming integers.

    The second example, you are adding 'd' after the numbers to tell the compiler that they are doubles so then you will get accurate math ;)
     
    Last edited by a moderator: Feb 2, 2016
  12. Immanuel-Scholz

    Immanuel-Scholz

    Joined:
    Jun 8, 2013
    Posts:
    221
    Aisaka Taiga: You are not testing with a float value of 0 but 0.02f.

    Yes, float has imprecisions (like double, but more visible). That is not on dispute here.

    What is the question is, whether adding the literal 0f to any float value can ever change that value and the answer should be "no" for basically all platforms in existance.

    (In other words: It's not about 10 / 3 * 3. Its about 10 + 0 - 0 which should always give 10.)
     
  13. Deleted User

    Deleted User

    Guest

    I see no issues here add 0f to .7f. If I reverse it so handAngularVelocity starts at 0 and it adds .7 each cycle, then the same floating point precision issues start back up.




    I setup the same test in Unity:
     
  14. abertrand

    abertrand

    Joined:
    Dec 7, 2015
    Posts:
    29
    I think you're right, 0.001f is indeed the very first value I assigned to the acceleration variable in its declaration so that's probably what's going on. What's counter-intuitive to me is that I never changed the value in the inspector - only ever did so in the code declaration - but I imagine that the first historically assigned value is like the "overriding-inspector value" for the MonoBehaviour.
     
  15. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Once a value is set in the inspector, it's set, even if it's the initial value. It's not an overriding inspector value, the inspector is showing you the value of the variable.