patterncppMinor
Generic and accurate floating point "equality"
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
The math library includes the function
You'd obviously only want to use this method for small values of
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
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.
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.