Search code examples
c#unity-game-enginefloating-pointprecisionfloating-accuracy

Is Mathf.Approximately(0.0f, float.Epsilon) == true its correct behavior?


I have just noticed that the following code returns true:

Mathf.Approximately(0.0f, float.Epsilon); // true

I have read the Mathf.Approximately Documentation and it states that:

Approximately() compares two floats and returns true if they are within a small value (Epsilon) of each other.

And Mathf.Epsilon Documentation states that:

  • anyValue + Epsilon = anyValue
  • anyValue - Epsilon = anyValue
  • 0 + Epsilon = Epsilon
  • 0 - Epsilon = -Epsilon

As a result, I ran the following code, expecting it to be false, but it also returns true.

Mathf.Approximately(0.0f, 2.0f * float.Epsilon); // true

By the way:

Mathf.Approximately(0.0f, 2.0f * float.Epsilon); // true
Mathf.Approximately(0.0f, 3.0f * float.Epsilon); // true
Mathf.Approximately(0.0f, 4.0f * float.Epsilon); // true
Mathf.Approximately(0.0f, 5.0f * float.Epsilon); // true
Mathf.Approximately(0.0f, 6.0f * float.Epsilon); // true
Mathf.Approximately(0.0f, 7.0f * float.Epsilon); // true

Mathf.Approximately(0.0f, 8.0f * float.Epsilon); // false
Mathf.Approximately(0.0f, 9.0f * float.Epsilon); // false

Q: Based on that evidence, can I safely say that Mathf.Approximately is not correctly implemented according to its documentation*?

(* and as a result, I should move to different solutions, such as the one in Floating point comparison functions for C#)


Solution

  • Here is the decompiled code of Unity's public static bool Mathf.Approximately(float a, float b); You can see the * 8.0f at the end ^^, so a truely badly documented method indeed.

    /// <summary>
    /// <para>Compares two floating point values if they are similar.</para>
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    public static bool Approximately(float a, float b)
    {
       return (double) Mathf.Abs(b - a) < (double) Mathf.Max(1E-06f * Mathf.Max(Mathf.Abs(a),
           Mathf.Abs(b)), Mathf.Epsilon * 8.0f);
    }