float: correctly handle double comparisons
This commit is contained in:
parent
2ae1c5e465
commit
9a6c4572fa
26
float.cpp
26
float.cpp
@ -101,7 +101,7 @@ ieee_float<E, S>::almost_equal (floating_t a,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Based on the cynus `AlmostEqualUlps`
|
// Based on the cynus `AlmostEqual2sComplement`
|
||||||
template <unsigned int E, unsigned int S>
|
template <unsigned int E, unsigned int S>
|
||||||
bool
|
bool
|
||||||
ieee_float<E, S>::almost_equal (floating_t _a,
|
ieee_float<E, S>::almost_equal (floating_t _a,
|
||||||
@ -115,29 +115,39 @@ ieee_float<E, S>::almost_equal (floating_t _a,
|
|||||||
union {
|
union {
|
||||||
floating_t f;
|
floating_t f;
|
||||||
sint_t s;
|
sint_t s;
|
||||||
|
uint_t u;
|
||||||
} a, b;
|
} a, b;
|
||||||
|
|
||||||
a.f = _a;
|
a.f = _a;
|
||||||
b.f = _b;
|
b.f = _b;
|
||||||
|
|
||||||
|
// Special case the NaNs early so simplify diffs
|
||||||
if (std::isnan (a.f) || std::isnan (b.f))
|
if (std::isnan (a.f) || std::isnan (b.f))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Early out, as identity comparisons are reasonably common
|
||||||
if (a.s == b.s)
|
if (a.s == b.s)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Order as twos complement to avoid negative zero/zero comparison issues
|
// Re-base negative floats to be continuous against +ve/-ve 0
|
||||||
if (a.s < 0)
|
static const union {
|
||||||
a.s = (1L << (TOTAL_BITS - 1)) - a.s;
|
floating_t f;
|
||||||
if (b.s < 0)
|
sint_t s;
|
||||||
b.s = (1L << (TOTAL_BITS - 1)) - b.s;
|
} NEG_ZERO { -floating_t {0} };
|
||||||
|
|
||||||
uint_t diff = std::abs (a.s - b.s);
|
if (a.s < 0)
|
||||||
|
a.s = NEG_ZERO.s - a.s;
|
||||||
|
if (b.s < 0)
|
||||||
|
b.s = NEG_ZERO.s - b.s;
|
||||||
|
|
||||||
|
// Calculate ULP difference, but don't use abs(a.s - b.s) as it may cause
|
||||||
|
// signed overflow
|
||||||
|
uint_t diff = a.u > b.u ? a.u - b.u : b.u - a.u;
|
||||||
return diff <= ulps;
|
return diff <= ulps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
template class ieee_float< 5, 10>; // ieee_half
|
template class ieee_float< 5, 10>; // ieee_half
|
||||||
template class ieee_float< 8, 23>; // ieee_single;
|
template class ieee_float< 8, 23>; // ieee_single;
|
||||||
template class ieee_float<11, 52>; // ieee_double;
|
template class ieee_float<11, 52>; // ieee_double;
|
||||||
|
@ -8,28 +8,57 @@
|
|||||||
using std::sqrt;
|
using std::sqrt;
|
||||||
using std::numeric_limits;
|
using std::numeric_limits;
|
||||||
|
|
||||||
int
|
|
||||||
main (int, char **) {
|
|
||||||
std::cerr.precision (15);
|
|
||||||
std::cout.precision (15);
|
|
||||||
|
|
||||||
CHECK (!almost_equal (-2.0, 0.0));
|
void
|
||||||
CHECK (!almost_equal (-2.f, 0.f));
|
test_comparisons (void)
|
||||||
CHECK ( almost_equal ( 0.0, 0.0));
|
{
|
||||||
//CHECK_HARD ( almost_equal ( 0.0, numeric_limits<double>::min ()));
|
// Check pos/neg zeroes
|
||||||
CHECK ( almost_equal (numeric_limits<double>::infinity (),
|
CHECK (almost_equal ( 0.f, 0.f));
|
||||||
numeric_limits<double>::infinity ()));
|
CHECK (almost_equal ( 0.f, -0.f));
|
||||||
CHECK (!almost_equal (numeric_limits<double>::infinity (), 0.0));
|
CHECK (almost_equal (-0.f, 0.f));
|
||||||
CHECK (!almost_equal (numeric_limits<double>::quiet_NaN (),
|
CHECK (almost_equal (-0.f, -0.f));
|
||||||
numeric_limits<double>::quiet_NaN ()));
|
|
||||||
|
|
||||||
CHECK (almost_equal (0.f, -0.f));
|
CHECK (almost_equal ( 0., 0.));
|
||||||
CHECK (almost_equal (0., -0.));
|
CHECK (almost_equal ( 0., -0.));
|
||||||
|
CHECK (almost_equal (-0., 0.));
|
||||||
|
CHECK (almost_equal (-0., -0.));
|
||||||
|
|
||||||
|
// Check zero comparison with values near the expected cutoff
|
||||||
CHECK ( almost_zero (1e-45f));
|
CHECK ( almost_zero (1e-45f));
|
||||||
CHECK (!almost_zero (1e-40f));
|
CHECK (!almost_zero (1e-40f));
|
||||||
CHECK (!exactly_zero (1e-45f));
|
CHECK (!exactly_zero (1e-45f));
|
||||||
|
|
||||||
|
// Compare values a little away from zero
|
||||||
|
CHECK (!almost_equal (-2.0, 0.0));
|
||||||
|
CHECK (!almost_equal (-2.f, 0.f));
|
||||||
|
|
||||||
|
// Compare values at the maximum extreme
|
||||||
|
CHECK (!almost_equal (-std::numeric_limits<float>::max (), 0.f));
|
||||||
|
CHECK (!almost_equal (-std::numeric_limits<float>::max (),
|
||||||
|
std::numeric_limits<float>::max ()));
|
||||||
|
|
||||||
|
// Compare infinity values
|
||||||
|
CHECK ( almost_equal (numeric_limits<double>::infinity (),
|
||||||
|
numeric_limits<double>::infinity ()));
|
||||||
|
CHECK (!almost_equal (numeric_limits<double>::infinity (), 0.0));
|
||||||
|
|
||||||
|
// Compare NaNs
|
||||||
|
CHECK (!almost_equal (0., numeric_limits<double>::quiet_NaN ()));
|
||||||
|
CHECK (!almost_equal (numeric_limits<double>::quiet_NaN (), 0.));
|
||||||
|
|
||||||
|
CHECK (!almost_equal (numeric_limits<double>::quiet_NaN (),
|
||||||
|
numeric_limits<double>::quiet_NaN ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int, char **) {
|
||||||
|
// Max out the precision in case we trigger debug output
|
||||||
|
std::cerr.precision (std::numeric_limits<double>::digits10);
|
||||||
|
std::cout.precision (std::numeric_limits<double>::digits10);
|
||||||
|
|
||||||
|
test_comparisons ();
|
||||||
|
|
||||||
CHECK_EQ (min (-2, 0, 2), -2);
|
CHECK_EQ (min (-2, 0, 2), -2);
|
||||||
CHECK_EQ (max (-2, 0, 2), 2);
|
CHECK_EQ (max (-2, 0, 2), 2);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user