aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/include/uhd/utils/fp_compare_epsilon.ipp68
-rw-r--r--host/include/uhd/utils/math.hpp4
-rw-r--r--host/tests/fp_compare_epsilon_test.cpp15
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)