views:

184

answers:

5

Can someone point towards (or show) some good general floating point comparison functions in C# for comparing floating point values? I want to implement functions for IsEqual, IsGreater an IsLess. I also only really care about doubles not floats.

This is the best I've found so far for an IsEqual function.

EDIT: This is junk don't use it. See the accepted answer below.

public static bool IsEqual(this double a, double b, double epsilon)
{
  if ((a == 0) && (b == 0))
    return true;
  else if (double.IsNaN(a) || double.IsNaN(b))
    return false;
  else if (double.IsPositiveInfinity(a))
    return double.IsPositiveInfinity(b);
  else if (double.IsNegativeInfinity(a))
    return double.IsNegativeInfinity(b);
  else
    return Math.Abs(a - b) <= Math.Abs(a * epsilon);
}
A: 

I think your second option is the best bet. Generally in floating-point comparison you often only care that one value is within a certain tolerance of another value, controlled by the selection of epsilon.

Reinderien
+1  A: 

Although the second option is more general, the first option is better when you have an absolute tolerance, and when you have to execute many of these comparisons. If this comparison is say for every pixel in an image, the multiplication in the second options might slow your execution to unacceptable levels of performance.

Phillip Ngan
Peformance isn't a big issue for my applications I'm more concerned with correctness.
Kevin Gale
You need to fight your premature optimization instincts harder. Make it work correctly first, only then start even thinking about making it fast (if it's even an issue at all).
Michael Borgwardt
A: 

What about: b - delta < a && a < b + delta

sh_kamalh
+5  A: 

Writing a useful general-purpose floating point IsEqual is very, very hard, if not outright impossible. Your current code will fail badly for a==0. How the method should behave for such cases is really a matter of definition, and arguably the code would best be tailored for the specific domain use case.

For this kind of thing, you really, really need a good test suite. That's how I did it for The Floating-Point Guide, this is what I came up with in the end (Java code, should be easy enough to translate):

public static boolean nearlyEqual(float a, float b, float epsilon)
{
    final float absA = Math.abs(a);
    final float absB = Math.abs(b);
    final float diff = Math.abs(a - b);

    if (a * b == 0) { // a or b or both are zero
        // relative error is not meaningful here
        return diff < (epsilon * epsilon);
    } else { // use relative error
        return diff / (absA + absB) < epsilon;
    }
}

You can also find the test suite on the site.

Michael Borgwardt
Thanks. This isn't my code actually just something I found and I could see problems with it and I hoped that would spur someone to answer my question. Do you have any input or examples for IsLess and IsGreater functions?
Kevin Gale
@Kevin: no, but it's not all that difficult to come up with test cases yourself. It's an almost perfect example for unit testing and a test-first approach: any change that makes one case work has a very high chance to break another. Without a test suite, it's nearly impossible to get all cases to work at the same time.
Michael Borgwardt
+1  A: 

From Bruce Dawson's paper on comparing floats, you can also compare floats as integers. Closeness is determined by least significant bits.

public static bool AlmostEqual2sComplement( float a, float b, int maxDeltaBits ) 
{
    int aInt = BitConverter.ToInt32( BitConverter.GetBytes( a ), 0 );
    if ( aInt <  0 )
        aInt = Int32.MinValue - aInt;  // Int32.MinValue = 0x80000000

    int bInt = BitConverter.ToInt32( BitConverter.GetBytes( b ), 0 );
    if ( bInt < 0 )
        bInt = Int32.MinValue - bInt;

    int intDiff = Math.Abs( aInt - bInt );
    return intDiff <= ( 1 << maxDeltaBits );
}

EDIT: BitConverter is relatively slow. If you're willing to use unsafe code, then here is a very fast version:

    public static unsafe int FloatToInt32Bits( float f )
    {
        return *( (int*)&f );
    }

    public static bool AlmostEqual2sComplement( float a, float b, int maxDeltaBits )
    {
        int aInt = FloatToInt32Bits( a );
        if ( aInt < 0 )
            aInt = Int32.MinValue - aInt;

        int bInt = FloatToInt32Bits( b );
        if ( bInt < 0 )
            bInt = Int32.MinValue - bInt;

        int intDiff = Math.Abs( aInt - bInt );
        return intDiff <= ( 1 << maxDeltaBits );
    }
Andrew Wang
Interesting. I come across a few references that seem to say this may be the best way to do it (comparing as an integer type). Michael Borgwardt above also links to Dawson's paper. I wonder if the bit converting is very expensive?
Kevin Gale
BitConverter is slow. I've added a much faster version, but it uses unsafe code.
Andrew Wang
Thanks I'll consider it and it will be useful to others who find this question.
Kevin Gale