mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
fix parameter ordering inconsistency that could cause switching the parameters to give different results
This commit is contained in:
parent
4188d35caf
commit
875f143d74
|
@ -88,18 +88,19 @@ bool
|
||||||
float_equals(const LNumber &lhs, const RNumber &rhs,
|
float_equals(const LNumber &lhs, const RNumber &rhs,
|
||||||
int epsilon_scale = 20) // scale the "fuzzy" equality. Higher value gives a more tolerant comparison
|
int epsilon_scale = 20) // scale the "fuzzy" equality. Higher value gives a more tolerant comparison
|
||||||
{
|
{
|
||||||
|
// a type that lhs and rhs can agree on
|
||||||
|
using common_t = typename std::common_type<LNumber, RNumber>::type;
|
||||||
|
// asserts for sane usage
|
||||||
static_assert(std::is_floating_point<LNumber>::value || std::is_floating_point<RNumber>::value,
|
static_assert(std::is_floating_point<LNumber>::value || std::is_floating_point<RNumber>::value,
|
||||||
"Using this function with two integers is just wasting time. Use ==");
|
"Using this function with two integers is just wasting time. Use ==");
|
||||||
static_assert(std::is_floating_point<EpsilonType>::value,
|
static_assert(std::numeric_limits<EpsilonType>::epsilon() < common_t{1},
|
||||||
"Cannot extract epsilon from a number that isn't a floating point type");
|
"epsilon >= 1.0 will cause all comparisons to return true");
|
||||||
|
|
||||||
// NANs always compare false with themselves
|
// NANs always compare false with themselves
|
||||||
if (std::isnan(lhs) || std::isnan(rhs))
|
if (std::isnan(lhs) || std::isnan(rhs))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// a type that lhs and rhs can agree on
|
|
||||||
using common_t = typename std::common_type<LNumber, RNumber>::type;
|
|
||||||
// epsilon type defaults to float because even if both args are a higher precision type
|
// epsilon type defaults to float because even if both args are a higher precision type
|
||||||
// either or both could have been promoted by prior operations
|
// either or both could have been promoted by prior operations
|
||||||
// if a higher precision is required, the template type can be changed
|
// if a higher precision is required, the template type can be changed
|
||||||
|
@ -108,7 +109,9 @@ float_equals(const LNumber &lhs, const RNumber &rhs,
|
||||||
// epsilon for numeric_limits is valid when abs(x) <1.0, scaling only needs to be upwards
|
// epsilon for numeric_limits is valid when abs(x) <1.0, scaling only needs to be upwards
|
||||||
// in particular, this prevents a lhs of 0 from requiring an exact comparison
|
// in particular, this prevents a lhs of 0 from requiring an exact comparison
|
||||||
// additionally, a scale factor is applied.
|
// additionally, a scale factor is applied.
|
||||||
common_t scaled_fuzz = epsilon_scale * epsilon * max(xlnt::detail::abs<common_t>(lhs), common_t{1});
|
common_t scaled_fuzz = epsilon_scale * epsilon * max(max(xlnt::detail::abs<common_t>(lhs),
|
||||||
|
xlnt::detail::abs<common_t>(rhs)), // |max| of parameters.
|
||||||
|
common_t{1}); // clamp
|
||||||
return ((lhs + scaled_fuzz) >= rhs) && ((rhs + scaled_fuzz) >= lhs);
|
return ((lhs + scaled_fuzz) >= rhs) && ((rhs + scaled_fuzz) >= lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
{
|
{
|
||||||
register_test(test_float_equals_zero);
|
register_test(test_float_equals_zero);
|
||||||
register_test(test_float_equals_large);
|
register_test(test_float_equals_large);
|
||||||
|
register_test(test_float_equals_fairness);
|
||||||
register_test(test_min);
|
register_test(test_min);
|
||||||
register_test(test_max);
|
register_test(test_max);
|
||||||
register_test(test_abs);
|
register_test(test_abs);
|
||||||
|
@ -121,6 +122,32 @@ public:
|
||||||
xlnt_assert(!xlnt::detail::float_equals(nan, 1000.f));
|
xlnt_assert(!xlnt::detail::float_equals(nan, 1000.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_float_equals_fairness()
|
||||||
|
{
|
||||||
|
// tests for parameter ordering dependency
|
||||||
|
// (lhs ~= rhs) == (rhs ~= lhs)
|
||||||
|
const double test_val = 1.0;
|
||||||
|
const double test_diff_pass = 1.192092e-07; // should all pass with this
|
||||||
|
const double test_diff = 1.192093e-07; // difference enough to provide different results if the comparison is not "fair"
|
||||||
|
const double test_diff_fails = 1.192094e-07; // should all fail with this
|
||||||
|
|
||||||
|
// test_diff_pass
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>((test_val + test_diff_pass), test_val, 1));
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>(test_val, (test_val + test_diff_pass), 1));
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>(-(test_val + test_diff_pass), -test_val, 1));
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>(-test_val, -(test_val + test_diff_pass), 1));
|
||||||
|
// test_diff
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>((test_val + test_diff), test_val, 1));
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>(test_val, (test_val + test_diff), 1));
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>(-(test_val + test_diff), -test_val, 1));
|
||||||
|
xlnt_assert(xlnt::detail::float_equals<float>(-test_val, -(test_val + test_diff), 1));
|
||||||
|
// test_diff_fails
|
||||||
|
xlnt_assert(!xlnt::detail::float_equals<float>((test_val + test_diff_fails), test_val, 1));
|
||||||
|
xlnt_assert(!xlnt::detail::float_equals<float>(test_val, (test_val + test_diff_fails), 1));
|
||||||
|
xlnt_assert(!xlnt::detail::float_equals<float>(-(test_val + test_diff_fails), -test_val, 1));
|
||||||
|
xlnt_assert(!xlnt::detail::float_equals<float>(-test_val, -(test_val + test_diff_fails), 1));
|
||||||
|
}
|
||||||
|
|
||||||
void test_min()
|
void test_min()
|
||||||
{
|
{
|
||||||
// simple
|
// simple
|
||||||
|
|
Loading…
Reference in New Issue
Block a user