diff options
-rw-r--r-- | host/include/uhd/utils/fp_compare_epsilon.ipp | 68 | ||||
-rw-r--r-- | host/include/uhd/utils/math.hpp | 4 | ||||
-rw-r--r-- | host/tests/fp_compare_epsilon_test.cpp | 15 |
3 files changed, 55 insertions, 32 deletions
diff --git a/host/include/uhd/utils/fp_compare_epsilon.ipp b/host/include/uhd/utils/fp_compare_epsilon.ipp index 99e467df4..a00cb0696 100644 --- a/host/include/uhd/utils/fp_compare_epsilon.ipp +++ b/host/include/uhd/utils/fp_compare_epsilon.ipp @@ -9,6 +9,7 @@ #include <cmath> #include <typeinfo> + #pragma once @@ -49,15 +50,32 @@ namespace uhd { namespace math { namespace fp_compare { _epsilon = copy._epsilon; } - template<typename float_t> UHD_INLINE - bool operator==(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs) { - - bool lhs_compare = ((std::abs(lhs._value - rhs._value) / std::abs(lhs._value)) + template <typename float_t> + UHD_INLINE bool operator==( + fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs) + { + // If raw bit values are equal, then they're equal. This also catches + // the case where both are 0.0! + if (lhs._value == rhs._value) { + return true; + } + + // If one of them is within epsilon of zero, but the other is not, then + // they're also not equal. + if ((std::abs(lhs._value) <= lhs._epsilon && std::abs(rhs._value) > rhs._epsilon) + || (std::abs(lhs._value) > lhs._epsilon + && std::abs(rhs._value) <= rhs._epsilon)) { + return false; + } + + // In all other cases, we use the "close enough with tolerance epsilon" + // algorithm as described in math.hpp. + const bool lhs_compare = ((std::abs(lhs._value - rhs._value) / std::abs(lhs._value)) <= lhs._epsilon); - bool rhs_compare = ((std::abs(lhs._value - rhs._value) / std::abs(rhs._value)) + const bool rhs_compare = ((std::abs(lhs._value - rhs._value) / std::abs(rhs._value)) <= rhs._epsilon); - return (lhs_compare && rhs_compare); + return (lhs_compare || rhs_compare); } template<typename float_t> UHD_INLINE @@ -67,7 +85,7 @@ namespace uhd { namespace math { namespace fp_compare { template<typename float_t> UHD_INLINE bool operator<(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs) { - return (lhs._value + lhs._epsilon) < (rhs._value - rhs._epsilon); + return (lhs != rhs) && (lhs._value < rhs._value); } template<typename float_t> UHD_INLINE @@ -77,7 +95,7 @@ namespace uhd { namespace math { namespace fp_compare { template<typename float_t> UHD_INLINE bool operator>(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs) { - return (lhs._value - lhs._epsilon) > (rhs._value + rhs._epsilon); + return (lhs != rhs) && (lhs._value > rhs._value); } template<typename float_t> UHD_INLINE @@ -85,15 +103,10 @@ namespace uhd { namespace math { namespace fp_compare { return !(lhs < rhs); } - template<typename float_t> UHD_INLINE - bool operator==(fp_compare_epsilon<float_t> lhs, double rhs) { - - bool lhs_compare = ((std::abs(lhs._value - rhs) / std::abs(lhs._value)) - <= lhs._epsilon); - bool rhs_compare = ((std::abs(lhs._value - rhs) / std::abs(rhs)) - <= DOUBLE_PRECISION_EPSILON); - - return (lhs_compare && rhs_compare); + template <typename float_t> + UHD_INLINE bool operator==(fp_compare_epsilon<float_t> lhs, double rhs) + { + return lhs == fp_compare_epsilon<float_t>(static_cast<float_t>(rhs)); } template<typename float_t> UHD_INLINE @@ -104,7 +117,7 @@ namespace uhd { namespace math { namespace fp_compare { template<typename float_t> UHD_INLINE bool operator<(fp_compare_epsilon<float_t> lhs, double rhs) { - return (lhs._value + lhs._epsilon) < (rhs - DOUBLE_PRECISION_EPSILON); + return (lhs != rhs) && (lhs._value < rhs); } template<typename float_t> UHD_INLINE @@ -115,7 +128,7 @@ namespace uhd { namespace math { namespace fp_compare { template<typename float_t> UHD_INLINE bool operator>(fp_compare_epsilon<float_t> lhs, double rhs) { - return (lhs._value - lhs._epsilon) > (rhs + DOUBLE_PRECISION_EPSILON); + return (lhs != rhs) && (lhs._value > rhs); } template<typename float_t> UHD_INLINE @@ -123,15 +136,10 @@ namespace uhd { namespace math { namespace fp_compare { return !(lhs < rhs); } - template<typename float_t> UHD_INLINE - bool operator==(double lhs, fp_compare_epsilon<float_t> rhs) { - - bool lhs_compare = ((std::abs(lhs - rhs._value) / std::abs(lhs)) - <= DOUBLE_PRECISION_EPSILON); - bool rhs_compare = ((std::abs(lhs - rhs._value) / std::abs(rhs._value)) - <= rhs._epsilon); - - return (lhs_compare && rhs_compare); + template <typename float_t> + UHD_INLINE bool operator==(double lhs, fp_compare_epsilon<float_t> rhs) + { + return fp_compare_epsilon<float_t>(static_cast<float_t>(lhs)) == rhs; } template<typename float_t> UHD_INLINE @@ -142,7 +150,7 @@ namespace uhd { namespace math { namespace fp_compare { template<typename float_t> UHD_INLINE bool operator<(double lhs, fp_compare_epsilon<float_t> rhs) { - return (lhs + DOUBLE_PRECISION_EPSILON) < (rhs._value - rhs._epsilon); + return (lhs != rhs) && (lhs < rhs._value); } template<typename float_t> UHD_INLINE @@ -153,7 +161,7 @@ namespace uhd { namespace math { namespace fp_compare { template<typename float_t> UHD_INLINE bool operator>(double lhs, fp_compare_epsilon<float_t> rhs) { - return (lhs - DOUBLE_PRECISION_EPSILON) > (rhs._value + rhs._epsilon); + return (lhs != rhs) && (lhs > rhs._value); } template<typename float_t> UHD_INLINE diff --git a/host/include/uhd/utils/math.hpp b/host/include/uhd/utils/math.hpp index 6c8fceae9..6ee46e98a 100644 --- a/host/include/uhd/utils/math.hpp +++ b/host/include/uhd/utils/math.hpp @@ -85,10 +85,10 @@ public: * There are obviously a lot of strategies for defining floating point * equality, and in the end it all comes down to the domain at hand. UHD's * floating-point-with-epsilon comparison algorithm is based on the method - * presented in Knuth's "The Art of Computer Science" called "very close + * presented in Knuth's "The Art of Computer Science" called "close enough * with tolerance epsilon". * - * [(|u - v| / |u|) <= e] && [(|u - v| / |v|) <= e] + * [(|u - v| / |u|) <= e] || [(|u - v| / |v|) <= e] * * UHD's modification to this algorithm is using the denominator's epsilon * value (since each float_t object has its own epsilon) for each diff --git a/host/tests/fp_compare_epsilon_test.cpp b/host/tests/fp_compare_epsilon_test.cpp index 0c2a7f1cb..3aa973d3e 100644 --- a/host/tests/fp_compare_epsilon_test.cpp +++ b/host/tests/fp_compare_epsilon_test.cpp @@ -75,6 +75,8 @@ BOOST_AUTO_TEST_CASE(float_equality_operators) fp_compare_epsilon<float>(alpha._value + uhd::math::SINGLE_PRECISION_EPSILON); BOOST_CHECK(not(alpha == charlie)); BOOST_CHECK(not(alpha == float(alpha._value + uhd::math::SINGLE_PRECISION_EPSILON))); + + BOOST_CHECK(fp_compare_epsilon<float>(0.0) == 0.0); } BOOST_AUTO_TEST_CASE(double_equality_operators) @@ -90,6 +92,8 @@ BOOST_AUTO_TEST_CASE(double_equality_operators) fp_compare_epsilon<double>(alpha._value + uhd::math::DOUBLE_PRECISION_EPSILON); BOOST_CHECK(not(alpha == charlie)); BOOST_CHECK(not(alpha == double(alpha._value + uhd::math::DOUBLE_PRECISION_EPSILON))); + + BOOST_CHECK(fp_compare_epsilon<double>(0.0) == 0.0); } BOOST_AUTO_TEST_CASE(float_inequality_operators) @@ -126,6 +130,8 @@ BOOST_AUTO_TEST_CASE(float_lessthan_operators) BOOST_CHECK(not(alpha < charlie)); BOOST_CHECK(not(alpha < float(alpha._value - 1.2f))); + + BOOST_CHECK(fp_compare_epsilon<float>(-0.1) < 0.0f); } BOOST_AUTO_TEST_CASE(double_lessthan_operators) @@ -143,6 +149,11 @@ BOOST_AUTO_TEST_CASE(double_lessthan_operators) BOOST_CHECK(not(alpha < charlie)); BOOST_CHECK(not(alpha < double(alpha._value - 1.0012))); + + fp_compare_epsilon<double> foxtrot(1.0, 0.001); + BOOST_CHECK(foxtrot < 1.0015); + + BOOST_CHECK(fp_compare_epsilon<double>(-0.1) < 0.0); } BOOST_AUTO_TEST_CASE(float_lessthanequals_operators) @@ -159,6 +170,8 @@ BOOST_AUTO_TEST_CASE(float_lessthanequals_operators) BOOST_CHECK(charlie <= alpha); BOOST_CHECK(float(alpha._value - 1.2) <= alpha); + + BOOST_CHECK(fp_compare_epsilon<double>(-0.01) < 0.0); } BOOST_AUTO_TEST_CASE(double_lessthanequals_operators) @@ -209,6 +222,8 @@ BOOST_AUTO_TEST_CASE(double_greaterthan_operators) BOOST_CHECK(not(alpha > charlie)); BOOST_CHECK(not(alpha > double(alpha._value + 0.0012))); + + BOOST_CHECK(fp_compare_epsilon<double>(0.01) > 0.0); } BOOST_AUTO_TEST_CASE(float_greaterthanequals_operators) |