What is the most effective way for float and double comparison?

Viewed 576154

What would be the most efficient way to compare two double or two float values?

Simply doing this is not correct:

bool CompareDoubles1 (double A, double B)
{
   return A == B;
}

But something like:

bool CompareDoubles2 (double A, double B) 
{
   diff = A - B;
   return (diff < EPSILON) && (-diff < EPSILON);
}

Seems to waste processing.

Does anyone know a smarter float comparer?

34 Answers

Be extremely careful using any of the other suggestions. It all depends on context.

I have spent a long time tracing bugs in a system that presumed a==b if |a-b|<epsilon. The underlying problems were:

  1. The implicit presumption in an algorithm that if a==b and b==c then a==c.

  2. Using the same epsilon for lines measured in inches and lines measured in mils (.001 inch). That is a==b but 1000a!=1000b. (This is why AlmostEqual2sComplement asks for the epsilon or max ULPS).

  3. The use of the same epsilon for both the cosine of angles and the length of lines!

  4. Using such a compare function to sort items in a collection. (In this case using the builtin C++ operator == for doubles produced correct results.)

Like I said: it all depends on context and the expected size of a and b.

By the way, std::numeric_limits<double>::epsilon() is the "machine epsilon". It is the difference between 1.0 and the next value representable by a double. I guess that it could be used in the compare function but only if the expected values are less than 1. (This is in response to @cdv's answer...)

Also, if you basically have int arithmetic in doubles (here we use doubles to hold int values in certain cases) your arithmetic will be correct. For example 4.0/2.0 will be the same as 1.0+1.0. This is as long as you do not do things that result in fractions (4.0/3.0) or do not go outside of the size of an int.

The comparison with an epsilon value is what most people do (even in game programming).

You should change your implementation a little though:

bool AreSame(double a, double b)
{
    return fabs(a - b) < EPSILON;
}

Edit: Christer has added a stack of great info on this topic on a recent blog post. Enjoy.

Comparing floating point numbers for depends on the context. Since even changing the order of operations can produce different results, it is important to know how "equal" you want the numbers to be.

Comparing floating point numbers by Bruce Dawson is a good place to start when looking at floating point comparison.

The following definitions are from The art of computer programming by Knuth:

bool approximatelyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool essentiallyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyGreaterThan(float a, float b, float epsilon)
{
    return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyLessThan(float a, float b, float epsilon)
{
    return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

Of course, choosing epsilon depends on the context, and determines how equal you want the numbers to be.

Another method of comparing floating point numbers is to look at the ULP (units in last place) of the numbers. While not dealing specifically with comparisons, the paper What every computer scientist should know about floating point numbers is a good resource for understanding how floating point works and what the pitfalls are, including what ULP is.

For a more in depth approach read Comparing floating point numbers. Here is the code snippet from that link:

// Usable AlmostEqual function    
bool AlmostEqual2sComplement(float A, float B, int maxUlps)    
{    
    // Make sure maxUlps is non-negative and small enough that the    
    // default NAN won't compare as equal to anything.    
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);    
    int aInt = *(int*)&A;    
    // Make aInt lexicographically ordered as a twos-complement int    
    if (aInt < 0)    
        aInt = 0x80000000 - aInt;    
    // Make bInt lexicographically ordered as a twos-complement int    
    int bInt = *(int*)&B;    
    if (bInt < 0)    
        bInt = 0x80000000 - bInt;    
    int intDiff = abs(aInt - bInt);    
    if (intDiff <= maxUlps)    
        return true;    
    return false;    
}

The portable way to get epsilon in C++ is

#include <limits>
std::numeric_limits<double>::epsilon()

Then the comparison function becomes

#include <cmath>
#include <limits>

bool AreSame(double a, double b) {
    return std::fabs(a - b) < std::numeric_limits<double>::epsilon();
}

The code you wrote is bugged :

return (diff < EPSILON) && (-diff > EPSILON);

The correct code would be :

return (diff < EPSILON) && (diff > -EPSILON);

(...and yes this is different)

I wonder if fabs wouldn't make you lose lazy evaluation in some case. I would say it depends on the compiler. You might want to try both. If they are equivalent in average, take the implementation with fabs.

If you have some info on which of the two float is more likely to be bigger than then other, you can play on the order of the comparison to take better advantage of the lazy evaluation.

Finally you might get better result by inlining this function. Not likely to improve much though...

Edit: OJ, thanks for correcting your code. I erased my comment accordingly

`return fabs(a - b) < EPSILON;

This is fine if:

  • the order of magnitude of your inputs don't change much
  • very small numbers of opposite signs can be treated as equal

But otherwise it'll lead you into trouble. Double precision numbers have a resolution of about 16 decimal places. If the two numbers you are comparing are larger in magnitude than EPSILON*1.0E16, then you might as well be saying:

return a==b;

I'll examine a different approach that assumes you need to worry about the first issue and assume the second is fine your application. A solution would be something like:

#define VERYSMALL  (1.0E-150)
#define EPSILON    (1.0E-8)
bool AreSame(double a, double b)
{
    double absDiff = fabs(a - b);
    if (absDiff < VERYSMALL)
    {
        return true;
    }

    double maxAbs  = max(fabs(a) - fabs(b));
    return (absDiff/maxAbs) < EPSILON;
}

This is expensive computationally, but it is sometimes what is called for. This is what we have to do at my company because we deal with an engineering library and inputs can vary by a few dozen orders of magnitude.

Anyway, the point is this (and applies to practically every programming problem): Evaluate what your needs are, then come up with a solution to address your needs -- don't assume the easy answer will address your needs. If after your evaluation you find that fabs(a-b) < EPSILON will suffice, perfect -- use it! But be aware of its shortcomings and other possible solutions too.

Here's proof that using std::numeric_limits::epsilon() is not the answer — it fails for values greater than one:

Proof of my comment above:

#include <stdio.h>
#include <limits>

double ItoD (__int64 x) {
    // Return double from 64-bit hexadecimal representation.
    return *(reinterpret_cast<double*>(&x));
}

void test (__int64 ai, __int64 bi) {
    double a = ItoD(ai), b = ItoD(bi);
    bool close = std::fabs(a-b) < std::numeric_limits<double>::epsilon();
    printf ("%.16f and %.16f %s close.\n", a, b, close ? "are " : "are not");
}

int main()
{
    test (0x3fe0000000000000L,
          0x3fe0000000000001L);

    test (0x3ff0000000000000L,
          0x3ff0000000000001L);
}

Running yields this output:

0.5000000000000000 and 0.5000000000000001 are  close.
1.0000000000000000 and 1.0000000000000002 are not close.

Note that in the second case (one and just larger than one), the two input values are as close as they can possibly be, and still compare as not close. Thus, for values greater than 1.0, you might as well just use an equality test. Fixed epsilons will not save you when comparing floating-point values.

Qt implements two functions, maybe you can learn from them:

static inline bool qFuzzyCompare(double p1, double p2)
{
    return (qAbs(p1 - p2) <= 0.000000000001 * qMin(qAbs(p1), qAbs(p2)));
}

static inline bool qFuzzyCompare(float p1, float p2)
{
    return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2)));
}

And you may need the following functions, since

Note that comparing values where either p1 or p2 is 0.0 will not work, nor does comparing values where one of the values is NaN or infinity. If one of the values is always 0.0, use qFuzzyIsNull instead. If one of the values is likely to be 0.0, one solution is to add 1.0 to both values.

static inline bool qFuzzyIsNull(double d)
{
    return qAbs(d) <= 0.000000000001;
}

static inline bool qFuzzyIsNull(float f)
{
    return qAbs(f) <= 0.00001f;
}

General-purpose comparison of floating-point numbers is generally meaningless. How to compare really depends on a problem at hand. In many problems, numbers are sufficiently discretized to allow comparing them within a given tolerance. Unfortunately, there are just as many problems, where such trick doesn't really work. For one example, consider working with a Heaviside (step) function of a number in question (digital stock options come to mind) when your observations are very close to the barrier. Performing tolerance-based comparison wouldn't do much good, as it would effectively shift the issue from the original barrier to two new ones. Again, there is no general-purpose solution for such problems and the particular solution might require going as far as changing the numerical method in order to achieve stability.

You have to do this processing for floating point comparison, since float's can't be perfectly compared like integer types. Here are functions for the various comparison operators.

Floating Point Equal to (==)

I also prefer the subtraction technique rather than relying on fabs() or abs(), but I'd have to speed profile it on various architectures from 64-bit PC to ATMega328 microcontroller (Arduino) to really see if it makes much of a performance difference.

So, let's forget about all this absolute value stuff and just do some subtraction and comparison!

Modified from Microsoft's example here:

/// @brief      See if two floating point numbers are approximately equal.
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  A small value such that if the difference between the two numbers is
///                      smaller than this they can safely be considered to be equal.
/// @return     true if the two numbers are approximately equal, and false otherwise
bool is_float_eq(float a, float b, float epsilon) {
    return ((a - b) < epsilon) && ((b - a) < epsilon);
}
bool is_double_eq(double a, double b, double epsilon) {
    return ((a - b) < epsilon) && ((b - a) < epsilon);
}

Example usage:

constexpr float EPSILON = 0.0001; // 1e-4
is_float_eq(1.0001, 0.99998, EPSILON);

I'm not entirely sure, but it seems to me some of the criticisms of the epsilon-based approach, as described in the comments below this highly-upvoted answer, can be resolved by using a variable epsilon, scaled according to the floating point values being compared, like this:

float a = 1.0001;
float b = 0.99998;
float epsilon = std::max(std::fabs(a), std::fabs(b)) * 1e-4;

is_float_eq(a, b, epsilon);

This way, the epsilon value scales with the floating point values and is therefore never so small of a value that it becomes insignificant.

For completeness, let's add the rest:

Greater than (>), and less than (<):

/// @brief      See if floating point number `a` is > `b`
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  a small value such that if `a` is > `b` by this amount, `a` is considered
///             to be definitively > `b`
/// @return     true if `a` is definitively > `b`, and false otherwise
bool is_float_gt(float a, float b, float epsilon) {
    return a > b + epsilon;
}
bool is_double_gt(double a, double b, double epsilon) {
    return a > b + epsilon;
}

/// @brief      See if floating point number `a` is < `b`
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  a small value such that if `a` is < `b` by this amount, `a` is considered
///             to be definitively < `b`
/// @return     true if `a` is definitively < `b`, and false otherwise
bool is_float_lt(float a, float b, float epsilon) {
    return a < b - epsilon;
}
bool is_double_lt(double a, double b, double epsilon) {
    return a < b - epsilon;
}

Greater than or equal to (>=), and less than or equal to (<=)

/// @brief      Returns true if `a` is definitively >= `b`, and false otherwise
bool is_float_ge(float a, float b, float epsilon) {
    return a > b - epsilon;
}
bool is_double_ge(double a, double b, double epsilon) {
    return a > b - epsilon;
}

/// @brief      Returns true if `a` is definitively <= `b`, and false otherwise
bool is_float_le(float a, float b, float epsilon) {
    return a < b + epsilon;
}
bool is_double_le(double a, double b, double epsilon) {
    return a < b + epsilon;
}

Additional improvements:

  1. A good default value for epsilon in C++ is std::numeric_limits<T>::epsilon(), which evaluates to either 0 or FLT_EPSILON, DBL_EPSILON, or LDBL_EPSILON. See here: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon. You can also see the float.h header for FLT_EPSILON, DBL_EPSILON, and LDBL_EPSILON.
    1. See https://en.cppreference.com/w/cpp/header/cfloat and
    2. https://www.cplusplus.com/reference/cfloat/
  2. You could template the functions instead, to handle all floating point types: float, double, and long double, with type checks for these types via a static_assert() inside the template.
  3. Scaling the epsilon value is a good idea to ensure it works for really large and really small a and b values. This article recommends and explains it: http://realtimecollisiondetection.net/blog/?p=89. So, you should scale epsilon by a scaling value equal to max(1.0, abs(a), abs(b)), as that article explains. Otherwise, as a and/or b increase in magnitude, the epsilon would eventually become so small relative to those values that it becomes lost in the floating point error. So, we scale it to become larger in magnitude like they are. However, using 1.0 as the smallest allowed scaling factor for epsilon also ensures that for really small-magnitude a and b values, epsilon itself doesn't get scaled so small that it also becomes lost in the floating point error. So, we limit the minimum scaling factor to 1.0.
  4. If you want to "encapsulate" the above functions into a class, don't. Instead, wrap them up in a namespace if you like in order to namespace them. Ex: if you put all of the stand-alone functions into a namespace called float_comparison, then you could access the is_eq() function like this, for instance: float_comparison::is_eq(1.0, 1.5);.
  5. It might also be nice to add comparisons against zero, not just comparisons between two values.
  6. So, here is a better type of solution with the above improvements in place:
    namespace float_comparison {
    
    /// Scale the epsilon value to become large for large-magnitude a or b, 
    /// but no smaller than 1.0, per the explanation above, to ensure that 
    /// epsilon doesn't ever fall out in floating point error as a and/or b
    /// increase in magnitude.
    template<typename T>
    static constexpr T scale_epsilon(T a, T b, T epsilon = 
        std::numeric_limits<T>::epsilon()) noexcept 
    {
        static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
            "require type float, double, or long double.");
        T scaling_factor;
        // Special case for when a or b is infinity
        if (std::isinf(a) || std::isinf(b)) 
        {
            scaling_factor = 0;
        } 
        else 
        {
            scaling_factor = std::max({(T)1.0, std::abs(a), std::abs(b)});
        }
    
        T epsilon_scaled = scaling_factor * std::abs(epsilon);
        return epsilon_scaled;
    }
    
    // Compare two values
    
    /// Equal: returns true if a is approximately == b, and false otherwise
    template<typename T>
    static constexpr bool is_eq(T a, T b, T epsilon = 
        std::numeric_limits<T>::epsilon()) noexcept 
    {
        static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
            "require type float, double, or long double.");
        // test `a == b` first to see if both a and b are either infinity 
        // or -infinity
        return a == b || std::abs(a - b) <= scale_epsilon(a, b, epsilon);
    }
    
    /* 
    etc. etc.:
    is_eq()
    is_ne()
    is_lt()
    is_le()
    is_gt()
    is_ge()
    */
    
    // Compare against zero
    
    /// Equal: returns true if a is approximately == 0, and false otherwise
    template<typename T>
    static constexpr bool is_eq_zero(T a, T epsilon = 
        std::numeric_limits<T>::epsilon()) noexcept 
    {
        static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
            "require type float, double, or long double.");
        return is_eq(a, (T)0.0, epsilon);
    }
    
    /* 
    etc. etc.:
    is_eq_zero()
    is_ne_zero()
    is_lt_zero()
    is_le_zero()
    is_gt_zero()
    is_ge_zero()
    */
    
    } // namespace float_comparison
    

See also:

  1. The macro forms of some of the functions above in my repo here: utilities.h.
    1. UPDATE 29 NOV 2020: it's a work-in-progress, and I'm going to make it a separate answer when ready, but I've produced a better, scaled-epsilon version of all of the functions in C in this file here: utilities.c. Take a look.
  2. ADDITIONAL READING I need to do now have done: Floating-point tolerances revisited, by Christer Ericson. VERY USEFUL ARTICLE! It talks about scaling epsilon in order to ensure it never falls out in floating point error, even for really large-magnitude a and/or b values!

Found another interesting implementation on: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon

#include <cmath>
#include <limits>
#include <iomanip>
#include <iostream>
#include <type_traits>
#include <algorithm>



template<class T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
    almost_equal(T x, T y, int ulp)
{
    // the machine epsilon has to be scaled to the magnitude of the values used
    // and multiplied by the desired precision in ULPs (units in the last place)
    return std::fabs(x-y) <= std::numeric_limits<T>::epsilon() * std::fabs(x+y) * ulp
        // unless the result is subnormal
        || std::fabs(x-y) < std::numeric_limits<T>::min();
}

int main()
{
    double d1 = 0.2;
    double d2 = 1 / std::sqrt(5) / std::sqrt(5);
    std::cout << std::fixed << std::setprecision(20) 
        << "d1=" << d1 << "\nd2=" << d2 << '\n';

    if(d1 == d2)
        std::cout << "d1 == d2\n";
    else
        std::cout << "d1 != d2\n";

    if(almost_equal(d1, d2, 2))
        std::cout << "d1 almost equals d2\n";
    else
        std::cout << "d1 does not almost equal d2\n";
}

I use this code. Unlike the above answers this allows one to give a abs_relative_error that is explained in the comments of the code.

The first version compares complex numbers, so that the error can be explained in terms of the angle between two "vectors" of the same length in the complex plane (which gives a little insight). Then from there the correct formula for two real numbers follows.

https://github.com/CarloWood/ai-utils/blob/master/almost_equal.h

The latter then is

template<class T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
   almost_equal(T x, T y, T const abs_relative_error)
{
  return 2 * std::abs(x - y) <= abs_relative_error * std::abs(x + y);
}

where abs_relative_error is basically (twice) the absolute value of what comes closest to being defined in the literature: a relative error. But that is just the choice of the name.

What it really is seen most clearly in the complex plane I think. If |x| = 1, and y lays in a circle around x with diameter abs_relative_error, then the two are considered equal.

I use the following function for floating-point numbers comparison:

bool approximatelyEqual(double a, double b)
{
  return fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits<double>::epsilon());
}

It depends on how precise you want the comparison to be. If you want to compare for exactly the same number, then just go with ==. (You almost never want to do this unless you actually want exactly the same number.) On any decent platform you can also do the following:

diff= a - b; return fabs(diff)<EPSILON;

as fabs tends to be pretty fast. By pretty fast I mean it is basically a bitwise AND, so it better be fast.

And integer tricks for comparing doubles and floats are nice but tend to make it more difficult for the various CPU pipelines to handle effectively. And it's definitely not faster on certain in-order architectures these days due to using the stack as a temporary storage area for values that are being used frequently. (Load-hit-store for those who care.)

My way may not be correct but useful

Convert both float to strings and then do string compare

bool IsFlaotEqual(float a, float b, int decimal)
{
    TCHAR form[50] = _T("");
    _stprintf(form, _T("%%.%df"), decimal);


    TCHAR a1[30] = _T(""), a2[30] = _T("");
    _stprintf(a1, form, a);
    _stprintf(a2, form, b);

    if( _tcscmp(a1, a2) == 0 )
        return true;

    return false;

}

operator overlaoding can also be done

I write this for java, but maybe you find it useful. It uses longs instead of doubles, but takes care of NaNs, subnormals, etc.

public static boolean equal(double a, double b) {
    final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
    final long sm = 0x8000000000000000L;    // sign mask
    final long cm = 0x8000000000000L;       // most significant decimal bit mask
    long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);        
    int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
    if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN 
    if (c == d) return true;                            // identical - fast check
    if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
    if ((c & sm) != (d & sm)) return false;             // different signs
    if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
    d <<= 12; c <<= 12;
    if (ea < eb) c = c >> 1 | sm;
    else if (ea > eb) d = d >> 1 | sm;
    c -= d;
    return c < 65536 && c > -65536;     // don't use abs(), because:
    // There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }

Keep in mind that after a number of floating-point operations, number can be very different from what we expect. There is no code to fix that.

This is another solution with lambda:

#include <cmath>
#include <limits>

auto Compare = [](float a, float b, float epsilon = std::numeric_limits<float>::epsilon()){ return (std::fabs(a - b) <= epsilon); };

How about this?

template<typename T>
bool FloatingPointEqual( T a, T b ) { return !(a < b) && !(b < a); }

I've seen various approaches - but never seen this, so I'm curious to hear of any comments too!

In this version you check, that numbers differ from one another not more that for some fraction (say, 0.0001%):

bool floatApproximatelyEquals(const float a, const float b) {
    if (b == 0.) return a == 0.; // preventing division by zero
    return abs(1. - a / b) < 1e-6;
}

Please note Sneftel's comment about possible fraction limits for float.

Also note, that it differs from approach with absolute epsilons - here you don't bother about "order of magnitude" - numbers might be, say 1e100, or 1e-100, they will always be compared consistently, and you don't have to update epsilon for every case.

Related