HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Generic and accurate floating point "equality"

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
genericequalitypointfloatingaccurateand

Problem

Like those who have come before me, I foolishly have sought to implement a generic, efficient, idiomatic, and most importantly correct method of comparing floating point numbers for equality. Like those who have come before me, I have most likely failed in at least one of those regards. I'd appreciate comments on any aspect of the following code

#include 
#include 
#include 
#include 
#include 

template
struct cond
{
    static constexpr bool value = B;
    using default_type = DEFAULT;
    using type = T;
};

template
struct select
{
    using type = 
        typename std::conditional::type>::type;
};

template 
struct select
{
    using type = T;
};

template 
struct select>
{
    using type = typename std::conditional::type;
};

template 
using select_t = typename select::type;

template 
struct SelectIntegralOfSameSize {
    using type = select_t,
        cond,
        cond,
        cond
    >;
    static_assert(!std::is_void::value, "Could not find sane integral type");
};

template ::type>
union Fp_Int_Union {
    FP fp_;
    I i_;
};

template 
bool nearlyEqual(FP left, FP right, FP maxDiff, std::size_t maxUlpsDiff) {
    // handle NaNs and infinities
    if (!std::isfinite(left) || !std::isfinite(right)) {
        return false;
    }

    // ULP comparison breaks for different signs
    if ((left  l, r;
    l.fp_ = left;
    r.fp_ = right;

    return std::abs(l.i_ - r.i_) <= maxUlpsDiff;
}

Solution

Consider using std::nextafter()

The math library includes the function std::nextafter() that will give you the next floating point number with the smallest possible, strictly positive difference with the given number. This is much safer than union-casting a float into an int, because there is no guarantee that two close floating point numbers will also be close as integers. In particular, that is only true if the exponent of both floating point numbers is the same. For example:

bool closeEnough = false;

if (left = right) {
            closeEnough = true;
            break;
        }
        left = std::nextafter(left);
    } while (maxUlpsDiff--);
} else {
    // handle the left > right case
}

return closeEnough;


You'd obviously only want to use this method for small values of maxUlpsDiff.

Is it really what you need?

You are trying to check whether two floating point numbers are close, for two possible ways of closeness: floating point difference, and ULP distance. Is that really what you need? Are you sure that the values you choose for maxDiff and maxUlpsDiff are exactly right? If not, you might get false positives or negatives.

Can you reformulate your floating point calculations into integer calculations? Integers may have a limitted range, but at least they are exact. Maybe you can use them to perform fixed-point arithmetic, or perhaps you can use two integers to form a rational number.

Code Snippets

bool closeEnough = false;

if (left <= right) {
    do {
        if (left >= right) {
            closeEnough = true;
            break;
        }
        left = std::nextafter(left);
    } while (maxUlpsDiff--);
} else {
    // handle the left > right case
}

return closeEnough;

Context

StackExchange Code Review Q#125011, answer score: 4

Revisions (0)

No revisions yet.