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) | 
