aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorBen Hilburn <ben.hilburn@ettus.com>2014-06-27 17:44:04 -0700
committerMartin Braun <martin.braun@ettus.com>2014-10-07 14:56:47 +0200
commit800f58430fdc577a2edd717d27b5902f75a1b1e4 (patch)
treee8436d8e008ab5e2220e67dbea1c4ee44d620cef /host
parent4448843ca7392e2e5fbd44d6af12e69e6d465e00 (diff)
downloaduhd-800f58430fdc577a2edd717d27b5902f75a1b1e4.tar.gz
uhd-800f58430fdc577a2edd717d27b5902f75a1b1e4.tar.bz2
uhd-800f58430fdc577a2edd717d27b5902f75a1b1e4.zip
math: Added a new uhd::math namespace + float comparison routines
* Float comparison is applied to tuning logic in DSP cores. * Properly using INT_MAX/MIN constants, defined in utils/math.hpp
Diffstat (limited to 'host')
-rw-r--r--host/include/uhd/utils/CMakeLists.txt1
-rw-r--r--host/include/uhd/utils/algorithm.hpp9
-rw-r--r--host/include/uhd/utils/fp_compare_delta.ipp175
-rw-r--r--host/include/uhd/utils/fp_compare_epsilon.ipp177
-rw-r--r--host/include/uhd/utils/math.hpp246
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.cpp12
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.cpp12
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_200.cpp12
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.cpp12
-rw-r--r--host/tests/CMakeLists.txt2
-rw-r--r--host/tests/fp_compare_delta_test.cpp250
-rw-r--r--host/tests/fp_compare_epsilon_test.cpp238
12 files changed, 1114 insertions, 32 deletions
diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt
index e9633286f..c308c9cde 100644
--- a/host/include/uhd/utils/CMakeLists.txt
+++ b/host/include/uhd/utils/CMakeLists.txt
@@ -27,6 +27,7 @@ UHD_INSTALL(FILES
gain_group.hpp
images.hpp
log.hpp
+ math.hpp
msg.hpp
msg_task.hpp
paths.hpp
diff --git a/host/include/uhd/utils/algorithm.hpp b/host/include/uhd/utils/algorithm.hpp
index 5598d862b..704d745d9 100644
--- a/host/include/uhd/utils/algorithm.hpp
+++ b/host/include/uhd/utils/algorithm.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2011 Ettus Research LLC
+// Copyright 2010-2014 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -19,15 +19,16 @@
#define INCLUDED_UHD_UTILS_ALGORITHM_HPP
#include <algorithm>
+#include <boost/cstdint.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
/*!
- * Useful templated functions and classes that I like to pretend are part of stl.
- * Many of the range wrapper functions come with recent versions of boost (1.43).
+ * Useful templated functions, classes, and constants. Some of these overlap
+ * with the STL, but these are created with Boost for portability.
+ * Many of the range wrapper functions come with versions of boost >= 1.43.
*/
namespace uhd{
-
/*!
* A wrapper around std::sort that takes a range instead of an iterator.
*
diff --git a/host/include/uhd/utils/fp_compare_delta.ipp b/host/include/uhd/utils/fp_compare_delta.ipp
new file mode 100644
index 000000000..092ade6e9
--- /dev/null
+++ b/host/include/uhd/utils/fp_compare_delta.ipp
@@ -0,0 +1,175 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/msg.hpp>
+#include <cmath>
+#include <typeinfo>
+
+#ifndef INCLUDED_UHD_UTILS_FLOAT_COMPARE_DELTA_IPP
+#define INCLUDED_UHD_UTILS_FLOAT_COMPARE_DELTA_IPP
+
+
+namespace uhd { namespace math { namespace fp_compare {
+
+ template<typename float_t> UHD_INLINE
+ float_t fp_compare_select_delta(float_t lhs_delta, float_t rhs_delta) {
+ return ((lhs_delta < rhs_delta) ? lhs_delta : rhs_delta);
+ }
+
+ template<> UHD_INLINE
+ fp_compare_delta<float>::fp_compare_delta(float value) {
+
+ _value = value;
+ _delta = SINGLE_PRECISION_DELTA;
+ }
+
+ template<> UHD_INLINE
+ fp_compare_delta<double>::fp_compare_delta(double value) {
+ _value = value;
+ _delta = DOUBLE_PRECISION_DELTA;
+ }
+
+ template<typename float_t> UHD_INLINE
+ fp_compare_delta<float_t>::fp_compare_delta(float_t value, float_t delta)
+ : _value(value),
+ _delta(delta)
+ { /* NOP */ }
+
+ template<typename float_t> UHD_INLINE
+ fp_compare_delta<float_t>::fp_compare_delta(const fp_compare_delta<float_t>& copy)
+ : _value(copy._value),
+ _delta(copy._delta)
+ { /* NOP */ }
+
+ template<typename float_t> UHD_INLINE
+ fp_compare_delta<float_t>::~fp_compare_delta()
+ { /* NOP */ }
+
+ template<typename float_t> UHD_INLINE
+ void fp_compare_delta<float_t>::operator=(const fp_compare_delta<float_t>& copy) {
+ _value = copy._value;
+ _delta = copy._delta;
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs) {
+ float_t delta = fp_compare_select_delta(lhs._delta, rhs._delta);
+ return (std::fabs(lhs._value - rhs._value) < delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator!=(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs) {
+ return !(lhs == rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs) {
+ float_t delta = fp_compare_select_delta(lhs._delta, rhs._delta);
+ return ((rhs._value - lhs._value) > delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs) {
+ return !(lhs > rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs) {
+ float_t delta = fp_compare_select_delta(lhs._delta, rhs._delta);
+ return ((lhs._value - rhs._value) > delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs) {
+ return !(lhs < rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(fp_compare_delta<float_t> lhs, double rhs) {
+ float_t delta = fp_compare_select_delta(double(lhs._delta),
+ DOUBLE_PRECISION_DELTA);
+ return (std::fabs(lhs._value - rhs) < delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator!=(fp_compare_delta<float_t> lhs, double rhs) {
+ return !(lhs == rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<(fp_compare_delta<float_t> lhs, double rhs) {
+ float_t delta = fp_compare_select_delta(double(lhs._delta),
+ DOUBLE_PRECISION_DELTA);
+ return ((rhs - lhs._value) > delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_delta<float_t> lhs, double rhs) {
+ return !(lhs > rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>(fp_compare_delta<float_t> lhs, double rhs) {
+ float_t delta = fp_compare_select_delta(double(lhs._delta),
+ DOUBLE_PRECISION_DELTA);
+ return ((lhs._value - rhs) > delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_delta<float_t> lhs, double rhs) {
+ return !(lhs < rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(double lhs, fp_compare_delta<float_t> rhs) {
+ float_t delta = fp_compare_select_delta(DOUBLE_PRECISION_DELTA,
+ double(rhs._delta));
+ return (std::fabs(lhs - rhs._value) < delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator!=(double lhs, fp_compare_delta<float_t> rhs) {
+ return !(lhs == rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<(double lhs, fp_compare_delta<float_t> rhs) {
+ float_t delta = fp_compare_select_delta(DOUBLE_PRECISION_DELTA,
+ double(rhs._delta));
+ return ((rhs._value - lhs) > delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<=(double lhs, fp_compare_delta<float_t> rhs) {
+ return !(lhs > rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>(double lhs, fp_compare_delta<float_t> rhs) {
+ float_t delta = fp_compare_select_delta(DOUBLE_PRECISION_DELTA,
+ double(rhs._delta));
+ return ((lhs - rhs._value) > delta);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>=(double lhs, fp_compare_delta<float_t> rhs) {
+ return !(lhs < rhs);
+ }
+
+} } } //namespace uhd::math::fp_compare
+
+#endif /* INCLUDED_UHD_UTILS_FLOAT_COMPARE_DELTA_IPP */
diff --git a/host/include/uhd/utils/fp_compare_epsilon.ipp b/host/include/uhd/utils/fp_compare_epsilon.ipp
new file mode 100644
index 000000000..ff2d585db
--- /dev/null
+++ b/host/include/uhd/utils/fp_compare_epsilon.ipp
@@ -0,0 +1,177 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/msg.hpp>
+#include <cmath>
+#include <typeinfo>
+
+#ifndef INCLUDED_UHD_UTILS_FP_COMPARE_EPSILON_IPP
+#define INCLUDED_UHD_UTILS_FP_COMPARE_EPSILON_IPP
+
+
+namespace uhd { namespace math { namespace fp_compare {
+
+ template<> UHD_INLINE
+ fp_compare_epsilon<float>::fp_compare_epsilon(float value) {
+
+ _value = value;
+ _epsilon = SINGLE_PRECISION_EPSILON;
+ }
+
+ template<> UHD_INLINE
+ fp_compare_epsilon<double>::fp_compare_epsilon(double value) {
+ _value = value;
+ _epsilon = DOUBLE_PRECISION_EPSILON;
+ }
+
+ template<typename float_t> UHD_INLINE
+ fp_compare_epsilon<float_t>::fp_compare_epsilon(float_t value, float_t epsilon)
+ : _value(value),
+ _epsilon(epsilon)
+ { /* NOP */ }
+
+ template<typename float_t> UHD_INLINE
+ fp_compare_epsilon<float_t>::fp_compare_epsilon(const fp_compare_epsilon<float_t>& copy)
+ : _value(copy._value),
+ _epsilon(copy._epsilon)
+ { /* NOP */ }
+
+ template<typename float_t> UHD_INLINE
+ fp_compare_epsilon<float_t>::~fp_compare_epsilon()
+ { /* NOP */ }
+
+ template<typename float_t> UHD_INLINE
+ void fp_compare_epsilon<float_t>::operator=(const fp_compare_epsilon<float_t>& copy) {
+ _value = copy._value;
+ _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::fabs(lhs._value - rhs._value) / std::fabs(lhs._value))
+ <= lhs._epsilon);
+ bool rhs_compare = ((std::fabs(lhs._value - rhs._value) / std::fabs(rhs._value))
+ <= rhs._epsilon);
+
+ return (lhs_compare && rhs_compare);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator!=(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs) {
+ return !(lhs == rhs);
+ }
+
+ 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);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs) {
+ return !(lhs > rhs);
+ }
+
+ 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);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs) {
+ return !(lhs < rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(fp_compare_epsilon<float_t> lhs, double rhs) {
+
+ bool lhs_compare = ((std::fabs(lhs._value - rhs) / std::fabs(lhs._value))
+ <= lhs._epsilon);
+ bool rhs_compare = ((std::fabs(lhs._value - rhs) / std::fabs(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 == rhs);
+ }
+
+ 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);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_epsilon<float_t> lhs, double rhs) {
+ return !(lhs > rhs);
+ }
+
+ 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);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_epsilon<float_t> lhs, double rhs) {
+ return !(lhs < rhs);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(double lhs, fp_compare_epsilon<float_t> rhs) {
+
+ bool lhs_compare = ((std::fabs(lhs - rhs._value) / std::fabs(lhs))
+ <= DOUBLE_PRECISION_EPSILON);
+ bool rhs_compare = ((std::fabs(lhs - rhs._value) / std::fabs(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 !(lhs == rhs);
+ }
+
+ 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);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator<=(double lhs, fp_compare_epsilon<float_t> rhs) {
+ return !(lhs > rhs);
+ }
+
+ 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);
+ }
+
+ template<typename float_t> UHD_INLINE
+ bool operator>=(double lhs, fp_compare_epsilon<float_t> rhs) {
+ return !(lhs < rhs);
+ }
+
+} } } //namespace uhd::math::fp_compare
+
+#endif /* INCLUDED_UHD_UTILS_FP_COMPARE_EPSILON_IPP */
diff --git a/host/include/uhd/utils/math.hpp b/host/include/uhd/utils/math.hpp
new file mode 100644
index 000000000..21825c3dc
--- /dev/null
+++ b/host/include/uhd/utils/math.hpp
@@ -0,0 +1,246 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_UHD_UTILS_MATH_HPP
+#define INCLUDED_UHD_UTILS_MATH_HPP
+
+#include <uhd/config.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/numeric/conversion/bounds.hpp>
+
+
+namespace uhd {
+
+/*!
+ * Contains useful mathematical functions, classes, and constants, which should
+ * be used in UHD when portable / `std` options are not available.
+ */
+namespace math {
+
+ /*!
+ * Numeric limits of certain types.
+ *
+ * There are many sources for getting these, including std::numeric_limts,
+ * <cstdint>, <climits>, and Boost. The <cstdint> option is preferable as it
+ * gives us fixed-width constants, but unfortunately is new as of C++11.
+ * Since this isn't available on many systems, we need to use one of the
+ * other options. We will use the Boost option, here, since we use Boost
+ * data types for portability across UHD.
+ */
+ static const boost::int32_t BOOST_INT32_MAX = boost::numeric::bounds<boost::int32_t>::highest();
+ static const boost::int32_t BOOST_INT32_MIN = boost::numeric::bounds<boost::int32_t>::lowest();
+
+ /*!
+ * Define epsilon values for floating point comparisons.
+ *
+ * There are a lot of different sources for epsilon values that we could use
+ * for this. For single-precision (f32), most machines will report an
+ * epsilon of 1.192e-7, and for double-precision (f64) most machines will
+ * report an epsilon of 2.220e-16. The issue is that these are not always
+ * appropriate, depending on the scale of the operands and how they have
+ * been rounded in previous calculations. The values defined here are
+ * defaults, but should be overridden for calculations depending on the
+ * application.
+ *
+ * If a particular comparison is operating using very small or very large
+ * values, a custom epsilon should be defined for those computations. This
+ * use-case is provided for in the `fp_compare_epsilon` class constructor.
+ */
+ static const float SINGLE_PRECISION_EPSILON = 1.19e-7;
+ static const double DOUBLE_PRECISION_EPSILON = 2.22e-16;
+
+namespace fp_compare {
+
+ /*!
+ * Class for floating-point comparisons using an epsilon.
+ *
+ * At construction, you can specify the epsilon to use for the comparisons.
+ * This class, combined with the operators under it, allow for
+ * epsilon-comparisons of floats. An example is:
+ *
+ * // Compare floats 'x' and 'y'.
+ * bool x_equals_y = (fp_compare_epsilon<float>(x) == y);
+ *
+ * // Compare doubles 'x' and 'y'.
+ * bool x_equals_y = (fp_compare_epsilon<double>(x) == y);
+ */
+ template<typename float_t> class fp_compare_epsilon {
+ public:
+ UHD_INLINE fp_compare_epsilon(float_t value);
+ UHD_INLINE fp_compare_epsilon(float_t value, float_t epsilon);
+ UHD_INLINE fp_compare_epsilon(const fp_compare_epsilon<float_t>& copy);
+ UHD_INLINE ~fp_compare_epsilon();
+ UHD_INLINE void operator=(const fp_compare_epsilon& copy);
+
+ float_t _value;
+ float_t _epsilon;
+ };
+
+ /* A Note on Floating Point Equality with Epsilons
+ *
+ * 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
+ * with tolerance epsilon".
+ *
+ * [(|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
+ * comparison.
+ */
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator!=(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_epsilon<float_t> lhs, fp_compare_epsilon<float_t> rhs);
+
+ /* If these operators are used with floats, we rely on type promotion to
+ * double. */
+ template<typename float_t> UHD_INLINE
+ bool operator==(fp_compare_epsilon<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator!=(fp_compare_epsilon<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<(fp_compare_epsilon<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_epsilon<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>(fp_compare_epsilon<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_epsilon<float_t> lhs, double rhs);
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(double lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator!=(double lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<(double lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<=(double lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>(double lhs, fp_compare_epsilon<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>=(double lhs, fp_compare_epsilon<float_t> rhs);
+
+} // namespace fp_compare
+
+
+ /*!
+ * Define delta values for floating point comparisons.
+ *
+ * These are the default deltas used by the 'fp_compare_delta' class for
+ * single and double-precision floating point comparisons.
+ */
+ static const float SINGLE_PRECISION_DELTA = 1e-3;
+ static const double DOUBLE_PRECISION_DELTA = 1e-5;
+
+ /*! Floating-point delta to use for frequency comparisons. */
+ static const double FREQ_COMPARISON_DELTA_HZ = 0.1;
+
+
+namespace fp_compare {
+
+/*!
+ * Class for floating-point comparisons using a delta.
+ *
+ * At construction, you can specify the delta to use for the comparisons.
+ * This class, combined with the operators under it, allow for
+ * delta-comparisons of floats. An example is:
+ *
+ * // Compare floats 'x' and 'y'.
+ * bool x_equals_y = (fp_compare_delta<float>(x) == y);
+ *
+ * // Compare doubles 'x' and 'y'.
+ * bool x_equals_y = (fp_compare_delta<double>(x) == y);
+ */
+ template<typename float_t> class fp_compare_delta {
+ public:
+ UHD_INLINE fp_compare_delta(float_t value);
+ UHD_INLINE fp_compare_delta(float_t value, float_t delta);
+ UHD_INLINE fp_compare_delta(const fp_compare_delta<float_t>& copy);
+ UHD_INLINE ~fp_compare_delta();
+ UHD_INLINE void operator=(const fp_compare_delta& copy);
+
+ float_t _value;
+ float_t _delta;
+ };
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator!=(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_delta<float_t> lhs, fp_compare_delta<float_t> rhs);
+
+ /* If these operators are used with floats, we rely on type promotion to
+ * double. */
+ template<typename float_t> UHD_INLINE
+ bool operator==(fp_compare_delta<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator!=(fp_compare_delta<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<(fp_compare_delta<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<=(fp_compare_delta<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>(fp_compare_delta<float_t> lhs, double rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>=(fp_compare_delta<float_t> lhs, double rhs);
+
+ template<typename float_t> UHD_INLINE
+ bool operator==(double lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator!=(double lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<(double lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator<=(double lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>(double lhs, fp_compare_delta<float_t> rhs);
+ template<typename float_t> UHD_INLINE
+ bool operator>=(double lhs, fp_compare_delta<float_t> rhs);
+
+} // namespace fp_compare
+
+ UHD_INLINE bool frequencies_are_equal(double lhs, double rhs) {
+ return(fp_compare::fp_compare_delta<double>(lhs, FREQ_COMPARISON_DELTA_HZ)
+ == fp_compare::fp_compare_delta<double>(rhs, FREQ_COMPARISON_DELTA_HZ));
+ }
+
+} // namespace math
+} // namespace uhd
+
+#include "fp_compare_epsilon.ipp"
+#include "fp_compare_delta.ipp"
+
+#endif /* INCLUDED_UHD_UTILS_MATH_HPP */
diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp
index 160124b3e..6a36e8fa1 100644
--- a/host/lib/usrp/cores/rx_dsp_core_200.cpp
+++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp
@@ -18,9 +18,9 @@
#include "rx_dsp_core_200.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
+#include <uhd/utils/math.hpp>
#include <uhd/utils/msg.hpp>
#include <uhd/utils/safe_call.hpp>
-#include <uhd/utils/algorithm.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/thread/thread.hpp> //thread sleep
#include <boost/math/special_functions/round.hpp>
@@ -242,15 +242,13 @@ public:
boost::int32_t freq_word = 0;
static const double scale_factor = std::pow(2.0, 32);
- static const boost::int32_t int_max = boost::numeric::bounds<boost::int32_t>::highest();
- static const boost::int32_t int_min = boost::numeric::bounds<boost::int32_t>::lowest();
- if((freq / _tick_rate) >= (int_max / scale_factor)) {
+ if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
/* Operation would have caused a positive overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::highest();
+ freq_word = uhd::math::BOOST_INT32_MAX;
- } else if((freq / _tick_rate) <= (int_min / scale_factor)) {
+ } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
/* Operation would have caused a negative overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::lowest();
+ freq_word = uhd::math::BOOST_INT32_MIN;
} else {
/* The operation is safe. Perform normally. */
diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
index 6ce3c1d32..32866880f 100644
--- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp
+++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
@@ -18,9 +18,9 @@
#include "rx_dsp_core_3000.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
+#include <uhd/utils/math.hpp>
#include <uhd/utils/msg.hpp>
#include <uhd/utils/safe_call.hpp>
-#include <uhd/utils/algorithm.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/thread/thread.hpp> //thread sleep
#include <boost/math/special_functions/round.hpp>
@@ -210,15 +210,13 @@ public:
boost::int32_t freq_word = 0;
static const double scale_factor = std::pow(2.0, 32);
- static const boost::int32_t int_max = boost::numeric::bounds<boost::int32_t>::highest();
- static const boost::int32_t int_min = boost::numeric::bounds<boost::int32_t>::lowest();
- if((freq / _tick_rate) >= (int_max / scale_factor)) {
+ if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
/* Operation would have caused a positive overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::highest();
+ freq_word = uhd::math::BOOST_INT32_MAX;
- } else if((freq / _tick_rate) <= (int_min / scale_factor)) {
+ } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
/* Operation would have caused a negative overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::lowest();
+ freq_word = uhd::math::BOOST_INT32_MIN;
} else {
/* The operation is safe. Perform normally. */
diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp
index 3f397dd6a..2ef9f4406 100644
--- a/host/lib/usrp/cores/tx_dsp_core_200.cpp
+++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp
@@ -18,8 +18,8 @@
#include "tx_dsp_core_200.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
+#include <uhd/utils/math.hpp>
#include <uhd/utils/msg.hpp>
-#include <uhd/utils/algorithm.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/sign.hpp>
@@ -182,15 +182,13 @@ public:
boost::int32_t freq_word = 0;
static const double scale_factor = std::pow(2.0, 32);
- static const boost::int32_t int_max = boost::numeric::bounds<boost::int32_t>::highest();
- static const boost::int32_t int_min = boost::numeric::bounds<boost::int32_t>::lowest();
- if((freq / _tick_rate) >= (int_max / scale_factor)) {
+ if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
/* Operation would have caused a positive overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::highest();
+ freq_word = uhd::math::BOOST_INT32_MAX;
- } else if((freq / _tick_rate) <= (int_min / scale_factor)) {
+ } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
/* Operation would have caused a negative overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::lowest();
+ freq_word = uhd::math::BOOST_INT32_MIN;
} else {
/* The operation is safe. Perform normally. */
diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
index 0dc19f2c8..736205402 100644
--- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp
+++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
@@ -18,8 +18,8 @@
#include "tx_dsp_core_3000.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
+#include <uhd/utils/math.hpp>
#include <uhd/utils/msg.hpp>
-#include <uhd/utils/algorithm.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/sign.hpp>
@@ -147,15 +147,13 @@ public:
boost::int32_t freq_word = 0;
static const double scale_factor = std::pow(2.0, 32);
- static const boost::int32_t int_max = boost::numeric::bounds<boost::int32_t>::highest();
- static const boost::int32_t int_min = boost::numeric::bounds<boost::int32_t>::lowest();
- if((freq / _tick_rate) >= (int_max / scale_factor)) {
+ if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
/* Operation would have caused a positive overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::highest();
+ freq_word = uhd::math::BOOST_INT32_MAX;
- } else if((freq / _tick_rate) <= (int_min / scale_factor)) {
+ } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
/* Operation would have caused a negative overflow of int32. */
- freq_word = boost::numeric::bounds<boost::int32_t>::lowest();
+ freq_word = uhd::math::BOOST_INT32_MIN;
} else {
/* The operation is safe. Perform normally. */
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index 7c4815004..62544b69b 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -31,6 +31,8 @@ SET(test_sources
cast_test.cpp
dict_test.cpp
error_test.cpp
+ fp_compare_delta_test.cpp
+ fp_compare_epsilon_test.cpp
gain_group_test.cpp
msg_test.cpp
property_test.cpp
diff --git a/host/tests/fp_compare_delta_test.cpp b/host/tests/fp_compare_delta_test.cpp
new file mode 100644
index 000000000..9b009a79d
--- /dev/null
+++ b/host/tests/fp_compare_delta_test.cpp
@@ -0,0 +1,250 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/math.hpp>
+#include <boost/test/unit_test.hpp>
+
+using namespace uhd::math::fp_compare;
+
+BOOST_AUTO_TEST_CASE(fp_compare_delta_constructors) {
+ // Test default constructor
+ fp_compare_delta<float> alpha = fp_compare_delta<float>(7457392.0);
+ fp_compare_delta<float> beta = fp_compare_delta<float>(alpha._value);
+ BOOST_CHECK_EQUAL(alpha._value, beta._value);
+ BOOST_CHECK_EQUAL(alpha._delta, beta._delta);
+
+ // Test constructor with specified delta
+ fp_compare_delta<float> foxtrot = fp_compare_delta<float>(alpha._value,
+ uhd::math::SINGLE_PRECISION_DELTA);
+ fp_compare_delta<float> gamma = fp_compare_delta<float>(alpha._value,
+ 2 * uhd::math::SINGLE_PRECISION_DELTA);
+ BOOST_CHECK_EQUAL(alpha._delta, foxtrot._delta);
+ BOOST_CHECK(not (alpha._delta == gamma._delta));
+
+ // Test copy-constructor
+ fp_compare_delta<float> charlie = fp_compare_delta<float>(alpha);
+ BOOST_CHECK_EQUAL(alpha._value, charlie._value);
+ BOOST_CHECK_EQUAL(alpha._delta, charlie._delta);
+
+ // Test assignment operator
+ fp_compare_delta<float> delta = beta;
+ BOOST_CHECK_EQUAL(alpha._value, delta._value);
+ BOOST_CHECK_EQUAL(alpha._delta, delta._delta);
+}
+
+BOOST_AUTO_TEST_CASE(double_compare_constructors) {
+ // Test default constructor
+ fp_compare_delta<double> alpha = fp_compare_delta<double>(45739210286.0101);
+ fp_compare_delta<double> beta = fp_compare_delta<double>(alpha._value);
+ BOOST_CHECK_EQUAL(alpha._value, beta._value);
+ BOOST_CHECK_EQUAL(alpha._delta, beta._delta);
+
+ // Test constructor with specified delta
+ fp_compare_delta<double> foxtrot = fp_compare_delta<double>(alpha._value,
+ uhd::math::DOUBLE_PRECISION_DELTA);
+ fp_compare_delta<double> gamma = fp_compare_delta<double>(alpha._value, 2.0e-6);
+ BOOST_CHECK_EQUAL(alpha._delta, foxtrot._delta);
+ BOOST_CHECK(not (alpha._delta == gamma._delta));
+
+ // Test copy-constructor
+ fp_compare_delta<double> charlie = fp_compare_delta<double>(alpha);
+ BOOST_CHECK_EQUAL(alpha._value, charlie._value);
+ BOOST_CHECK_EQUAL(alpha._delta, charlie._delta);
+
+ // Test assignment operator
+ fp_compare_delta<double> delta = beta;
+ BOOST_CHECK_EQUAL(alpha._value, delta._value);
+ BOOST_CHECK_EQUAL(alpha._delta, delta._delta);
+}
+
+BOOST_AUTO_TEST_CASE(float_equality_operators) {
+ // Test basic equality operator
+ fp_compare_delta<float> alpha = fp_compare_delta<float>(1.0);
+ fp_compare_delta<float> beta = fp_compare_delta<float>(alpha._value);
+ BOOST_CHECK(alpha == beta);
+ BOOST_CHECK(alpha == float(alpha._value));
+
+ // Test equality edge case at difference = delta
+ fp_compare_delta<float> charlie = fp_compare_delta<float>(alpha._value
+ + uhd::math::SINGLE_PRECISION_DELTA);
+ BOOST_CHECK(not (alpha == charlie));
+ BOOST_CHECK(not (alpha == float(alpha._value + uhd::math::SINGLE_PRECISION_DELTA)));
+}
+
+BOOST_AUTO_TEST_CASE(double_equality_operators) {
+ // Test basic equality operator
+ fp_compare_delta<double> alpha = fp_compare_delta<double>(1.0);
+ fp_compare_delta<double> beta = fp_compare_delta<double>(alpha._value);
+ BOOST_CHECK(alpha == beta);
+ BOOST_CHECK(alpha == double(beta._value));
+
+ // Test equality edge case at delta = delta
+ fp_compare_delta<double> charlie = fp_compare_delta<double>(alpha._value
+ + uhd::math::DOUBLE_PRECISION_DELTA);
+ BOOST_CHECK(not (alpha == charlie));
+ BOOST_CHECK(not (alpha == double(alpha._value + uhd::math::DOUBLE_PRECISION_DELTA)));
+}
+
+BOOST_AUTO_TEST_CASE(float_inequality_operators) {
+ // Test inequality operator, which is based on equality operator
+ fp_compare_delta<float> alpha = fp_compare_delta<float>(127.0);
+ fp_compare_delta<float> beta = fp_compare_delta<float>(alpha._value + 1.19e-3);
+
+ BOOST_CHECK(alpha != beta);
+ BOOST_CHECK(alpha != float(alpha._value + 1.19e-3));
+}
+
+BOOST_AUTO_TEST_CASE(double_inequality_operators) {
+ // Test inequality operator, which is based on equality operator
+ fp_compare_delta<double> alpha = fp_compare_delta<double>(1.0);
+ fp_compare_delta<double> beta = fp_compare_delta<double>(alpha._value + 1.19e-5);
+
+ BOOST_CHECK(alpha != beta);
+ BOOST_CHECK(alpha != double(alpha._value + 1.19e-5));
+}
+
+BOOST_AUTO_TEST_CASE(float_lessthan_operators) {
+ // Test less-than operator
+ fp_compare_delta<float> alpha = fp_compare_delta<float>(274192.7);
+ fp_compare_delta<float> beta = fp_compare_delta<float>(alpha._value - 0.2);
+
+ BOOST_CHECK(beta < alpha);
+ BOOST_CHECK(float(alpha._value - 0.2) < alpha);
+
+ // Confirm false less-than case
+ fp_compare_delta<float> charlie = fp_compare_delta<float>(alpha._value - 1.2);
+
+ BOOST_CHECK(not (alpha < charlie));
+ BOOST_CHECK(not (alpha < float(alpha._value - 1.2)));
+}
+
+BOOST_AUTO_TEST_CASE(double_lessthan_operators) {
+ // Test less-than operator
+ fp_compare_delta<double> alpha = fp_compare_delta<double>(274192856.762312);
+ fp_compare_delta<double> beta = fp_compare_delta<double>(alpha._value - 0.0002);
+
+ BOOST_CHECK(beta < alpha);
+ BOOST_CHECK(double(alpha._value - 0.0002) < alpha);
+
+ // Confirm false less-than case
+ fp_compare_delta<double> charlie = fp_compare_delta<double>(alpha._value - 1.0012);
+
+ BOOST_CHECK(not (alpha < charlie));
+ BOOST_CHECK(not (alpha < double(alpha._value - 1.0012)));
+}
+
+BOOST_AUTO_TEST_CASE(float_lessthanequals_operators) {
+ // Test that <= correctly reports for equal values
+ fp_compare_delta<float> alpha = fp_compare_delta<float>(827.3);
+ fp_compare_delta<float> beta = fp_compare_delta<float>(alpha._value);
+
+ BOOST_CHECK(alpha <= beta);
+ BOOST_CHECK(alpha <= float(alpha._value));
+
+ // Test that <= correctly reports for less-than values
+ fp_compare_delta<float> charlie = fp_compare_delta<float>(alpha._value - 1.2);
+
+ BOOST_CHECK(charlie <= alpha);
+ BOOST_CHECK(float(alpha._value - 1.2) <= alpha);
+}
+
+BOOST_AUTO_TEST_CASE(double_lessthanequals_operators) {
+ // Test that <= correctly reports for equal values
+ fp_compare_delta<double> alpha = fp_compare_delta<double>(837652123.383764);
+ fp_compare_delta<double> beta = fp_compare_delta<double>(alpha._value);
+
+ BOOST_CHECK(alpha <= beta);
+ BOOST_CHECK(alpha <= double(alpha._value));
+
+ // Test that <= correctly reports for less-than values
+ fp_compare_delta<double> charlie = fp_compare_delta<double>(alpha._value - 0.0012);
+
+ BOOST_CHECK(charlie <= alpha);
+ BOOST_CHECK(double(alpha._value - 0.0012) <= alpha);
+}
+
+BOOST_AUTO_TEST_CASE(float_greaterthan_operators) {
+ // Test basic greater-than functionality
+ fp_compare_delta<float> alpha = fp_compare_delta<float>(98325.4);
+ fp_compare_delta<float> beta = fp_compare_delta<float>(alpha._value + 0.15);
+
+ BOOST_CHECK(beta > alpha);
+ BOOST_CHECK(float(alpha._value + 0.15) > alpha);
+
+ // Test false greater-than case
+ fp_compare_delta<float> charlie = fp_compare_delta<float>(alpha._value + 1.2);
+
+ BOOST_CHECK(not (alpha > charlie));
+ BOOST_CHECK(not (alpha > float(alpha._value + 1.2)));
+}
+
+BOOST_AUTO_TEST_CASE(double_greaterthan_operators) {
+ // Test basic greater-than functionality
+ fp_compare_delta<double> alpha = fp_compare_delta<double>(643907213.428475);
+ fp_compare_delta<double> beta = fp_compare_delta<double>(alpha._value + 0.0002);
+
+ BOOST_CHECK(beta > alpha);
+ BOOST_CHECK(double(alpha._value + 0.0002) > alpha);
+
+ // Test false greater-than case
+ fp_compare_delta<double> charlie = fp_compare_delta<double>(alpha._value + 0.0012);
+
+ BOOST_CHECK(not (alpha > charlie));
+ BOOST_CHECK(not (alpha > double(alpha._value + 0.0012)));
+}
+
+BOOST_AUTO_TEST_CASE(float_greaterthanequals_operators) {
+ // Test that >= correctly reports for equal values
+ fp_compare_delta<float> alpha = fp_compare_delta<float>(7834.89);
+ fp_compare_delta<float> beta = fp_compare_delta<float>(alpha._value);
+
+ BOOST_CHECK(alpha >= beta);
+ BOOST_CHECK(alpha >= float(alpha._value));
+
+ // Test that >= correctly reports for greater-than values
+ fp_compare_delta<float> charlie = fp_compare_delta<float>(alpha._value + 4.8);
+
+ BOOST_CHECK(charlie >= alpha);
+ BOOST_CHECK(float(alpha._value + 4.8) >= alpha);
+}
+
+BOOST_AUTO_TEST_CASE(double_greaterthanequals_operators) {
+ // Test that >= correctly reports for equal values
+ fp_compare_delta<double> alpha = fp_compare_delta<double>(737623834.89843);
+ fp_compare_delta<double> beta = fp_compare_delta<double>(alpha._value);
+
+ BOOST_CHECK(alpha >= beta);
+ BOOST_CHECK(alpha >= double(alpha._value));
+
+ // Test that >= correctly reports for greater-than values
+ fp_compare_delta<double> charlie = fp_compare_delta<double>(alpha._value + 3.0008);
+
+ BOOST_CHECK(charlie >= alpha);
+ BOOST_CHECK(double(alpha._value + 3.0008) >= alpha);
+}
+
+BOOST_AUTO_TEST_CASE(frequency_compare_function) {
+
+ BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232, 6817333232));
+ BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333233, 6817333232));
+ BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232.1, 6817333232.1));
+ BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333232.5, 6817333232.6));
+ BOOST_CHECK(uhd::math::frequencies_are_equal(16.8173332321e9, 16.8173332321e9));
+ BOOST_CHECK(!uhd::math::frequencies_are_equal(16.8173332322e9, 16.8173332321e9));
+ BOOST_CHECK(!uhd::math::frequencies_are_equal(5.0, 4.0));
+ BOOST_CHECK(uhd::math::frequencies_are_equal(48750000, 48749999.9946));
+}
diff --git a/host/tests/fp_compare_epsilon_test.cpp b/host/tests/fp_compare_epsilon_test.cpp
new file mode 100644
index 000000000..5790318c2
--- /dev/null
+++ b/host/tests/fp_compare_epsilon_test.cpp
@@ -0,0 +1,238 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/math.hpp>
+#include <boost/test/unit_test.hpp>
+
+using namespace uhd::math::fp_compare;
+
+BOOST_AUTO_TEST_CASE(fp_compare_epsilon_constructors) {
+ // Test default constructor
+ fp_compare_epsilon<float> alpha = fp_compare_epsilon<float>(7457392.0);
+ fp_compare_epsilon<float> beta = fp_compare_epsilon<float>(alpha._value);
+ BOOST_CHECK_EQUAL(alpha._value, beta._value);
+ BOOST_CHECK_EQUAL(alpha._epsilon, beta._epsilon);
+
+ // Test constructor with specified epsilon
+ fp_compare_epsilon<float> foxtrot = fp_compare_epsilon<float>(alpha._value,
+ uhd::math::SINGLE_PRECISION_EPSILON);
+ fp_compare_epsilon<float> gamma = fp_compare_epsilon<float>(alpha._value, 2.0e-1);
+ BOOST_CHECK_EQUAL(alpha._epsilon, foxtrot._epsilon);
+ BOOST_CHECK(not (alpha._epsilon == gamma._epsilon));
+
+ // Test copy-constructor
+ fp_compare_epsilon<float> charlie = fp_compare_epsilon<float>(alpha);
+ BOOST_CHECK_EQUAL(alpha._value, charlie._value);
+ BOOST_CHECK_EQUAL(alpha._epsilon, charlie._epsilon);
+
+ // Test assignment operator
+ fp_compare_epsilon<float> delta = beta;
+ BOOST_CHECK_EQUAL(alpha._value, delta._value);
+ BOOST_CHECK_EQUAL(alpha._epsilon, delta._epsilon);
+}
+
+BOOST_AUTO_TEST_CASE(double_compare_constructors) {
+ // Test default constructor
+ fp_compare_epsilon<double> alpha = fp_compare_epsilon<double>(45739210286.0101);
+ fp_compare_epsilon<double> beta = fp_compare_epsilon<double>(alpha._value);
+ BOOST_CHECK_EQUAL(alpha._value, beta._value);
+ BOOST_CHECK_EQUAL(alpha._epsilon, beta._epsilon);
+
+ // Test constructor with specified epsilon
+ fp_compare_epsilon<double> foxtrot = fp_compare_epsilon<double>(alpha._value,
+ uhd::math::DOUBLE_PRECISION_EPSILON);
+ fp_compare_epsilon<double> gamma = fp_compare_epsilon<double>(alpha._value, 2.0e-6);
+ BOOST_CHECK_EQUAL(alpha._epsilon, foxtrot._epsilon);
+ BOOST_CHECK(not (alpha._epsilon == gamma._epsilon));
+
+ // Test copy-constructor
+ fp_compare_epsilon<double> charlie = fp_compare_epsilon<double>(alpha);
+ BOOST_CHECK_EQUAL(alpha._value, charlie._value);
+ BOOST_CHECK_EQUAL(alpha._epsilon, charlie._epsilon);
+
+ // Test assignment operator
+ fp_compare_epsilon<double> delta = beta;
+ BOOST_CHECK_EQUAL(alpha._value, delta._value);
+ BOOST_CHECK_EQUAL(alpha._epsilon, delta._epsilon);
+}
+
+BOOST_AUTO_TEST_CASE(float_equality_operators) {
+ // Test basic equality operator
+ fp_compare_epsilon<float> alpha = fp_compare_epsilon<float>(1.0);
+ fp_compare_epsilon<float> beta = fp_compare_epsilon<float>(alpha._value);
+ BOOST_CHECK(alpha == beta);
+ BOOST_CHECK(alpha == float(alpha._value));
+
+ // Test equality edge case at delta = epsilon
+ fp_compare_epsilon<float> charlie = 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_AUTO_TEST_CASE(double_equality_operators) {
+ // Test basic equality operator
+ fp_compare_epsilon<double> alpha = fp_compare_epsilon<double>(1.0);
+ fp_compare_epsilon<double> beta = fp_compare_epsilon<double>(alpha._value);
+ BOOST_CHECK(alpha == beta);
+ BOOST_CHECK(alpha == double(beta._value));
+
+ // Test equality edge case at delta = epsilon
+ fp_compare_epsilon<double> charlie = 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_AUTO_TEST_CASE(float_inequality_operators) {
+ // Test inequality operator, which is based on equality operator
+ fp_compare_epsilon<float> alpha = fp_compare_epsilon<float>(127.0);
+ fp_compare_epsilon<float> beta = fp_compare_epsilon<float>(alpha._value + 1.19e-5);
+
+ BOOST_CHECK(alpha != beta);
+ BOOST_CHECK(alpha != float(alpha._value + 1.19e-5));
+}
+
+BOOST_AUTO_TEST_CASE(double_inequality_operators) {
+ // Test inequality operator, which is based on equality operator
+ fp_compare_epsilon<double> alpha = fp_compare_epsilon<double>(1.0);
+ fp_compare_epsilon<double> beta = fp_compare_epsilon<double>(alpha._value + 1.19e-10);
+
+ BOOST_CHECK(alpha != beta);
+ BOOST_CHECK(alpha != double(alpha._value + 1.19e-10));
+}
+
+BOOST_AUTO_TEST_CASE(float_lessthan_operators) {
+ // Test less-than operator
+ fp_compare_epsilon<float> alpha = fp_compare_epsilon<float>(274192.7);
+ fp_compare_epsilon<float> beta = fp_compare_epsilon<float>(alpha._value - 0.15);
+
+ BOOST_CHECK(beta < alpha);
+ BOOST_CHECK(float(alpha._value - 0.15) < alpha);
+
+ // Confirm false less-than case
+ fp_compare_epsilon<float> charlie = fp_compare_epsilon<float>(alpha._value - 1.2);
+
+ BOOST_CHECK(not (alpha < charlie));
+ BOOST_CHECK(not (alpha < float(alpha._value - 1.2)));
+}
+
+BOOST_AUTO_TEST_CASE(double_lessthan_operators) {
+ // Test less-than operator
+ fp_compare_epsilon<double> alpha = fp_compare_epsilon<double>(274192856.762312);
+ fp_compare_epsilon<double> beta = fp_compare_epsilon<double>(alpha._value - 0.0002);
+
+ BOOST_CHECK(beta < alpha);
+ BOOST_CHECK(double(alpha._value - 0.0002) < alpha);
+
+ // Confirm false less-than case
+ fp_compare_epsilon<double> charlie = fp_compare_epsilon<double>(alpha._value - 1.0012);
+
+ BOOST_CHECK(not (alpha < charlie));
+ BOOST_CHECK(not (alpha < double(alpha._value - 1.0012)));
+}
+
+BOOST_AUTO_TEST_CASE(float_lessthanequals_operators) {
+ // Test that <= correctly reports for equal values
+ fp_compare_epsilon<float> alpha = fp_compare_epsilon<float>(827.3);
+ fp_compare_epsilon<float> beta = fp_compare_epsilon<float>(alpha._value);
+
+ BOOST_CHECK(alpha <= beta);
+ BOOST_CHECK(alpha <= float(alpha._value));
+
+ // Test that <= correctly reports for less-than values
+ fp_compare_epsilon<float> charlie = fp_compare_epsilon<float>(alpha._value - 1.2);
+
+ BOOST_CHECK(charlie <= alpha);
+ BOOST_CHECK(float(alpha._value - 1.2) <= alpha);
+}
+
+BOOST_AUTO_TEST_CASE(double_lessthanequals_operators) {
+ // Test that <= correctly reports for equal values
+ fp_compare_epsilon<double> alpha = fp_compare_epsilon<double>(837652123.383764);
+ fp_compare_epsilon<double> beta = fp_compare_epsilon<double>(alpha._value);
+
+ BOOST_CHECK(alpha <= beta);
+ BOOST_CHECK(alpha <= double(alpha._value));
+
+ // Test that <= correctly reports for less-than values
+ fp_compare_epsilon<double> charlie = fp_compare_epsilon<double>(alpha._value - 0.0012);
+
+ BOOST_CHECK(charlie <= alpha);
+ BOOST_CHECK(double(alpha._value - 0.0012) <= alpha);
+}
+
+BOOST_AUTO_TEST_CASE(float_greaterthan_operators) {
+ // Test basic greater-than functionality
+ fp_compare_epsilon<float> alpha = fp_compare_epsilon<float>(98325.4);
+ fp_compare_epsilon<float> beta = fp_compare_epsilon<float>(alpha._value + 0.15);
+
+ BOOST_CHECK(beta > alpha);
+ BOOST_CHECK(float(alpha._value + 0.15) > alpha);
+
+ // Test false greater-than case
+ fp_compare_epsilon<float> charlie = fp_compare_epsilon<float>(alpha._value + 1.2);
+
+ BOOST_CHECK(not (alpha > charlie));
+ BOOST_CHECK(not (alpha > float(alpha._value + 1.2)));
+}
+
+BOOST_AUTO_TEST_CASE(double_greaterthan_operators) {
+ // Test basic greater-than functionality
+ fp_compare_epsilon<double> alpha = fp_compare_epsilon<double>(643907213.428475);
+ fp_compare_epsilon<double> beta = fp_compare_epsilon<double>(alpha._value + 0.0002);
+
+ BOOST_CHECK(beta > alpha);
+ BOOST_CHECK(double(alpha._value + 0.0002) > alpha);
+
+ // Test false greater-than case
+ fp_compare_epsilon<double> charlie = fp_compare_epsilon<double>(alpha._value + 0.0012);
+
+ BOOST_CHECK(not (alpha > charlie));
+ BOOST_CHECK(not (alpha > double(alpha._value + 0.0012)));
+}
+
+BOOST_AUTO_TEST_CASE(float_greaterthanequals_operators) {
+ // Test that >= correctly reports for equal values
+ fp_compare_epsilon<float> alpha = fp_compare_epsilon<float>(7834.89);
+ fp_compare_epsilon<float> beta = fp_compare_epsilon<float>(alpha._value);
+
+ BOOST_CHECK(alpha >= beta);
+ BOOST_CHECK(alpha >= float(alpha._value));
+
+ // Test that >= correctly reports for greater-than values
+ fp_compare_epsilon<float> charlie = fp_compare_epsilon<float>(alpha._value + 4.8);
+
+ BOOST_CHECK(charlie >= alpha);
+ BOOST_CHECK(float(alpha._value + 4.8) >= alpha);
+}
+
+BOOST_AUTO_TEST_CASE(double_greaterthanequals_operators) {
+ // Test that >= correctly reports for equal values
+ fp_compare_epsilon<double> alpha = fp_compare_epsilon<double>(737623834.89843);
+ fp_compare_epsilon<double> beta = fp_compare_epsilon<double>(alpha._value);
+
+ BOOST_CHECK(alpha >= beta);
+ BOOST_CHECK(alpha >= double(alpha._value));
+
+ // Test that >= correctly reports for greater-than values
+ fp_compare_epsilon<double> charlie = fp_compare_epsilon<double>(alpha._value + 3.0008);
+
+ BOOST_CHECK(charlie >= alpha);
+ BOOST_CHECK(double(alpha._value + 3.0008) >= alpha);
+}