0

So I've understood for a long time that floating point equality is not perfect in any programing language. But until recently after uncovering a bug at work related to this issue, I never realized just how bizarre these situations can be.

Here are some examples:

enter image description here

enter image description here

enter image description here

![enter image description here

I understand at a low level why this is happening. But for practical purposes in my application (and I would wager in most applications), we would want all four of the above examples to be true.

Most solutions I've found involve taking the absolute value of a two variables and adding a precision factor. For example:

var isEqual = Math.Abs(a - b) < 1e-15;

Or for greater than or equal to:

var isAGreaterThanB = (a + 1e-15) >= b;

But I've noticed that a few issues with this:

  • The above doesn't necessarily work when comparing double and float types
  • It can be difficult to understand, particularly as part of a larger expression

So my question is, what is the ideal way to determine practical equality for floating point numbers? I'm currently using C#, but would be interested in answers for other common programming languages as well.

My definition of ideal here is as follows:

  • Works 100% of the time
  • Easy to read/understand
  • High performance

Thanks!

nmg49
  • 1,356
  • 1
  • 11
  • 28
  • If you know the amount of decimal places to be displayed, why not use `Math.Round(a, foo)`. It will print true on a `double 0.3` and `float 0.3f` – jaabh Oct 22 '20 at 18:35
  • 1
    "Ideal" is opinion based ... also there is already similar question here something like "how to compare floats" – Selvin Oct 22 '20 at 18:41
  • @Selvin I explicitly define my criteria for "ideal" at the end of the question. If you have any input on how to better phase this, I would love to hear it. Regarding there being similar questions, I wasn't able to find anything on SO or elsewhere that answered this specific question. If you can find something I missed, please let me know. – nmg49 Oct 22 '20 at 18:54
  • If `closeEnough(x, y)` works with probability `P`, and `closeEnough(P, 100%)` is true, does that satisfy the "ideal" requirement? – Ben Voigt Oct 22 '20 at 19:13

2 Answers2

4

If there was a comparison that met your criteria

  • Works 100% of the time
  • Easy to read/understand
  • High performance

then your favorite programming language would already define comparison operators on floating-point types to do that.

But in reality, the "right" thing to do varies widely based on the meaning of the stored number. There is no one-size-fits-all relaxation of floating point equality!!!

You will have to think about the data you have, its accuracy, measurement error, quantization error, rounding error in calculations, and what decisions you are making with it. Trying to delegate this thinking to someone making general purpose tools is doomed to fail.

Even defining "equal enough" with deep domain knowledge is subject to a lot of pitfalls, for example you may1 find that this happens:


1 By "may" I mean this happens more often than your "ideal" floating-point comparison works.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
-2

You can create extensions for double and float with functions to do these checks for you adding a delta what will make your code more readable, something like this:

public static class FloatExt
{
    public static bool EqualsWithDelta(this float a, float b, float delta)
    {
        return Math.Abs(a - b) < delta;
    }

    public static bool EqualsWithDelta(this float a, double b, float delta)
    {
        return Math.Abs(a - b) < delta;
    }

    public static bool EqualsWithDelta(this double a, float b, float delta)
    {
        return Math.Abs(a - b) < delta;
    }

    public static bool EqualsWithDelta(this double a, double b, float delta)
    {
        return Math.Abs(a - b) < delta;
    }

    //For the rest I'm only implementing float/float, implement the rest of combinations
    public static bool GreaterWithDelta(this float a, float b, float delta)
    {
        return a - b > delta;
    }

    public static bool LesserWithDelta(this double a, double b, float delta)
    {
        return b - a > delta;
    }
}

With this you can write code easy to read and you can choose a different delta each time depending how much precission you need:

float value = 0.15f + 0.15f;
double valueb = 0.1 + 0.2;

Console.WriteLine(value.EqualsWithDelta(valueb, 0.00000001f));

var greater = valueb.GreaterWithDelta(value, 0.001f);
var lesser = value.LesserWithDelta(valueb, 0.000001f);
Gusman
  • 14,905
  • 2
  • 34
  • 50