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

5.0f * 0.02f = 0.09999999?

Discussion in 'Scripting' started by PhoenixRising1, Nov 13, 2015.

  1. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    Is there any way around this? Am I not supposed to calculate in this way?
     
  2. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
  3. Warsymphony

    Warsymphony

    Joined:
    Sep 23, 2014
    Posts:
    43
    That's just how floating point operations are. The float type can be particularly ill-suited for some kinds of calculations because it is impossible to represent some decimals as a float exactly.
     
    PhoenixRising1 likes this.
  4. Warsymphony

    Warsymphony

    Joined:
    Sep 23, 2014
    Posts:
    43
    If you want a greater degree of precision use the decimal type.
     
    PhoenixRising1 likes this.
  5. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    I assumed I did something wrong :p. The easiest way was to just use System.Math.Round for the output, but thanks guys.
     
  6. Warsymphony

    Warsymphony

    Joined:
    Sep 23, 2014
    Posts:
    43
    Your welcome! Careful with rounding only round your float if you know 100 percent it's precision isn't going to be exact. Otherwise you may be rounding exact decimals and getting incorrect results.
     
  7. Warsymphony

    Warsymphony

    Joined:
    Sep 23, 2014
    Posts:
    43
    This may be equally useful/easy

    5.0D * 0.02D = 1
     
    kfirmon likes this.
  8. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    True, but in this case it will always work, since it's either 0.099999 or 0.100001
     
  9. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    The problem is that I would have to change a lot of methods and events to use doubles instead of floats.
     
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    That's only circumstantial, doubles still suffer float error related to the issue OP has. It just doesn't show its face in this one situation.

    If you know the result is going to be 1, then why even do the arithmetic?

    The problem is that when using floats (doubles included) you should just accept that there will be float error. Otherwise use a more precise number type like Decimal or Integer (noting that they too come with their own errors, like ints can't represent fractions, and decimals still have repeating value errors like 1/3).

    In the end the whole problem comes down to storing infinite series in a finite space.

    In base 10, 1/3 is 0.3333333333..., well the computer only holds a finite amount of those digits, and 0.333333333 is not the same as 0.33333333...

    In base 2 0.1 is 0.000110011001100110011..., and 0.2 is 0.0011001100110011..., and again we lose the series, it has to fit in a finite space. So 0.2 in binary stored in a 32-bit space, does not actually equal 0.2... it's approximately 0.2.

    This is why the decimal type still has issues with 1/3rd. It's just that the issues are more obvious to the user since they're issues they've dealt with in the real world since we use decimals in the real world.

    The binary ones are harder to realize since we don't usually deal with binary in the real world... and thusly don't know what ratios are repeating values.
     
    Last edited: Nov 13, 2015
    PhoenixRising1 likes this.
  11. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I don't use any == comparisons at all, anywhere unless integer. You shouldn't either. Also 0.00001 etc delta isn't anything useful most of the time.
     
    PhoenixRising1 and lordofduct like this.
  12. Warsymphony

    Warsymphony

    Joined:
    Sep 23, 2014
    Posts:
    43
    Why don't you?
     
  13. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Because the nature of floating point means you can't compare floats directly, and the behaviour is different on different hardware?
     
  14. Warsymphony

    Warsymphony

    Joined:
    Sep 23, 2014
    Posts:
    43
    I see I thought you meant you don't use any == comparisons at all for any data type. XD
     
  15. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Hahaha! no! in context of thread :D
     
  16. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    It will not always be one, I meant to say: there will always be a small deviation, of like 0.000001, if at all. Thanks for teaching me about number types.
     
  17. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    So I should check if:

    Mathf.Abs(a - b) < 0.00001 for example then? Thanks for the info.
     
    Last edited: Nov 13, 2015
  18. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Yep I do exactly that.
     
    PhoenixRising1 likes this.
  19. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Yeah, I generalize them into my math util class with the name 'fuzzy':
    https://github.com/lordofduct/space.../blob/master/SpacepuppyBase/Utils/MathUtil.cs

    Code (csharp):
    1.  
    2.  
    3.         #region "Simple fuzzy arithmetic"
    4.  
    5.         /// <summary>
    6.         /// Test if float is kind of equal to some other value by some epsilon.
    7.         ///
    8.         /// Due to float error, two values may be considered similar... but the computer considers them different.
    9.         /// By using some epsilon (degree of error) once can test if the two values are similar.
    10.         /// </summary>
    11.         /// <param name="a"></param>
    12.         /// <param name="b"></param>
    13.         /// <param name="epsilon"></param>
    14.         /// <returns></returns>
    15.         /// <remarks></remarks>
    16.         public static bool FuzzyEqual(float a, float b, float epsilon)
    17.         {
    18.             return Math.Abs(a - b) < epsilon;
    19.         }
    20.  
    21.         public static bool FuzzyEqual(float a, float b)
    22.         {
    23.             return FuzzyEqual(a, b, EPSILON);
    24.         }
    25.  
    26.         /// <summary>
    27.         /// Test if float is less than some other value by some degree of error in epsilon.
    28.         ///
    29.         /// Due to float error, two values may be considered similar... but the computer considers them different.
    30.         /// By using some epsilon (degree of error) once can test if the two values are similar.
    31.         /// </summary>
    32.         /// <param name="a"></param>
    33.         /// <param name="b"></param>
    34.         /// <param name="epsilon"></param>
    35.         /// <param name="exclusive"></param>
    36.         /// <returns></returns>
    37.         /// <remarks></remarks>
    38.         public static bool FuzzyLessThan(float a, float b, float epsilon, bool exclusive)
    39.         {
    40.             //exclusive means we prefer to easily exclude a true result
    41.             if (exclusive)
    42.                 return a < b - epsilon;
    43.             else
    44.                 return a < b + epsilon;
    45.         }
    46.  
    47.         public static bool FuzzyLessThan(float a, float b, bool exclusive)
    48.         {
    49.             return FuzzyLessThan(a, b, EPSILON, exclusive);
    50.         }
    51.  
    52.         /// <summary>
    53.         /// Test if float is less than some other value by some degree of error in epsilon.
    54.         ///
    55.         /// Due to float error, two values may be considered similar... but the computer considers them different.
    56.         /// By using some epsilon (degree of error) once can test if the two values are similar.
    57.         /// </summary>
    58.         /// <param name="a"></param>
    59.         /// <param name="b"></param>
    60.         /// <param name="epsilon"></param>
    61.         /// <param name="exclusive"></param>
    62.         /// <returns></returns>
    63.         /// <remarks></remarks>
    64.         public static bool FuzzyGreaterThan(float a, float b, float epsilon, bool exclusive)
    65.         {
    66.             //exclusive means we prefer to easily exclude a true result
    67.             if (exclusive)
    68.                 return a > b + epsilon;
    69.             else
    70.                 return a > b - epsilon;
    71.         }
    72.  
    73.         public static bool FuzzyGreaterThan(float a, float b, bool exclusive)
    74.         {
    75.             return FuzzyGreaterThan(a, b, EPSILON, exclusive);
    76.         }
    77.  
    78.         /// <summary>
    79.         /// Test if a value is near some target value, if with in some range of 'epsilon', the target is returned.
    80.         ///
    81.         /// eg:
    82.         /// Slam(1.52,2,0.1) == 1.52
    83.         /// Slam(1.62,2,0.1) == 1.62
    84.         /// Slam(1.72,2,0.1) == 1.72
    85.         /// Slam(1.82,2,0.1) == 1.82
    86.         /// Slam(1.92,2,0.1) == 2
    87.         /// </summary>
    88.         /// <param name="value"></param>
    89.         /// <param name="target"></param>
    90.         /// <param name="epsilon"></param>
    91.         /// <returns></returns>
    92.         /// <remarks></remarks>
    93.         public static float Slam(float value, float target, float epsilon)
    94.         {
    95.             if (Math.Abs(value - target) < epsilon)
    96.             {
    97.                 return target;
    98.             }
    99.             else
    100.             {
    101.                 return value;
    102.             }
    103.         }
    104.  
    105.         public static float Slam(float value, float target)
    106.         {
    107.             return Slam(value, target, EPSILON);
    108.         }
    109.         #endregion
    110.  
    111.  
     
    PhoenixRising1 likes this.
  20. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    Nice, can I assume that this is true for comparing positions and velocities too then? I mean it has always worked to compare positions and checking if (velocity == Vector3.zero) before but I have a lot of work to do tonight if that is the case :).
     
  21. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    vector's use floats, so no, you shouldn't.

    Floats can compare, it's just there are chances it won't.

    For instance... if you set the velocity Vector3.zero, and than compare to Vector3.zero, it will resolve correctly. BUT if you subtract some velocity from it that makes it what you expect to be 0, it may not actually resolve to zero.

    Again I generalize my methods to test such things:
    https://github.com/lordofduct/space...lob/master/SpacepuppyBase/Utils/VectorUtil.cs

    Code (csharp):
    1.  
    2.         #region Compare Vector
    3.  
    4.         /// <summary>
    5.         /// Compares if a and b are nearly on the same axis and will probably return a zero vector from a cross product
    6.         /// </summary>
    7.         /// <param name="a"></param>
    8.         /// <param name="b"></param>
    9.         /// <param name="epsilon"></param>
    10.         /// <returns></returns>
    11.         public static bool NearSameAxis(Vector3 a, Vector3 b, float epsilon = MathUtil.EPSILON)
    12.         {
    13.             return MathUtil.FuzzyEqual(Mathf.Abs(Vector3.Dot(a.normalized, b.normalized)), 1.0f, epsilon);
    14.         }
    15.  
    16.         public static bool NearZeroVector(this Vector3 v)
    17.         {
    18.             return MathUtil.FuzzyEqual(v.sqrMagnitude, 0f, MathUtil.EPSILON_SQR);
    19.         }
    20.  
    21.         public static bool NearZeroVector(this Vector2 v)
    22.         {
    23.             return MathUtil.FuzzyEqual(v.sqrMagnitude, 0f, MathUtil.EPSILON_SQR);
    24.         }
    25.  
    26.         public static bool FuzzyEquals(this Vector2 a, Vector2 b)
    27.         {
    28.             return MathUtil.FuzzyEqual(Vector3.SqrMagnitude(a - b), 0f, MathUtil.EPSILON_SQR);
    29.         }
    30.  
    31.         public static bool FuzzyEquals(this Vector2 a, Vector2 b, float epsilon)
    32.         {
    33.             return MathUtil.FuzzyEqual(Vector3.SqrMagnitude(a - b), 0f, epsilon);
    34.         }
    35.  
    36.         public static bool FuzzyEquals(this Vector3 a, Vector3 b)
    37.         {
    38.             return MathUtil.FuzzyEqual(Vector3.SqrMagnitude(a - b), 0f, MathUtil.EPSILON_SQR);
    39.         }
    40.  
    41.         public static bool FuzzyEquals(this Vector3 a, Vector3 b, float epsilon)
    42.         {
    43.             return MathUtil.FuzzyEqual(Vector3.SqrMagnitude(a - b), 0f, epsilon);
    44.         }
    45.  
    46.         #endregion
    47.  
     
    PhoenixRising1 likes this.
  22. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    Thanks again.