summaryrefslogtreecommitdiff
path: root/boost/test/floating_point_comparison.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/test/floating_point_comparison.hpp')
-rw-r--r--boost/test/floating_point_comparison.hpp286
1 files changed, 286 insertions, 0 deletions
diff --git a/boost/test/floating_point_comparison.hpp b/boost/test/floating_point_comparison.hpp
new file mode 100644
index 0000000000..5446adac88
--- /dev/null
+++ b/boost/test/floating_point_comparison.hpp
@@ -0,0 +1,286 @@
+// (C) Copyright Gennadiy Rozental 2001-2008.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org/libs/test for the library home page.
+//
+// File : $RCSfile$
+//
+// Version : $Revision: 57992 $
+//
+// Description : defines algoirthms for comparing 2 floating point values
+// ***************************************************************************
+
+#ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
+#define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
+
+// Boost.Test
+#include <boost/test/detail/global_typedef.hpp>
+#include <boost/test/utils/class_properties.hpp>
+#include <boost/test/predicate_result.hpp>
+
+// Boost
+#include <boost/limits.hpp> // for std::numeric_limits
+#include <boost/numeric/conversion/conversion_traits.hpp> // for numeric::conversion_traits
+#include <boost/static_assert.hpp>
+
+#include <boost/test/detail/suppress_warnings.hpp>
+
+//____________________________________________________________________________//
+
+namespace boost {
+
+namespace test_tools {
+
+using unit_test::readonly_property;
+
+// ************************************************************************** //
+// ************** floating_point_comparison_type ************** //
+// ************************************************************************** //
+
+enum floating_point_comparison_type {
+ FPC_STRONG, // "Very close" - equation 1' in docs, the default
+ FPC_WEAK // "Close enough" - equation 2' in docs.
+
+};
+
+// ************************************************************************** //
+// ************** details ************** //
+// ************************************************************************** //
+
+namespace tt_detail {
+
+// FPT is Floating-Point Type: float, double, long double or User-Defined.
+template<typename FPT>
+inline FPT
+fpt_abs( FPT fpv )
+{
+ return fpv < static_cast<FPT>(0) ? -fpv : fpv;
+}
+
+//____________________________________________________________________________//
+
+template<typename FPT>
+struct fpt_limits {
+ static FPT min_value()
+ {
+ return std::numeric_limits<FPT>::is_specialized
+ ? (std::numeric_limits<FPT>::min)()
+ : 0;
+ }
+ static FPT max_value()
+ {
+ return std::numeric_limits<FPT>::is_specialized
+ ? (std::numeric_limits<FPT>::max)()
+ : static_cast<FPT>(1000000); // for the our purpuses it doesn't really matter what value is returned here
+ }
+};
+
+//____________________________________________________________________________//
+
+// both f1 and f2 are unsigned here
+template<typename FPT>
+inline FPT
+safe_fpt_division( FPT f1, FPT f2 )
+{
+ // Avoid overflow.
+ if( (f2 < static_cast<FPT>(1)) && (f1 > f2*fpt_limits<FPT>::max_value()) )
+ return fpt_limits<FPT>::max_value();
+
+ // Avoid underflow.
+ if( (f1 == static_cast<FPT>(0)) ||
+ ((f2 > static_cast<FPT>(1)) && (f1 < f2*fpt_limits<FPT>::min_value())) )
+ return static_cast<FPT>(0);
+
+ return f1/f2;
+}
+
+//____________________________________________________________________________//
+
+} // namespace tt_detail
+
+// ************************************************************************** //
+// ************** tolerance presentation types ************** //
+// ************************************************************************** //
+
+template<typename FPT>
+struct percent_tolerance_t {
+ explicit percent_tolerance_t( FPT v ) : m_value( v ) {}
+
+ FPT m_value;
+};
+
+//____________________________________________________________________________//
+
+template<typename Out,typename FPT>
+Out& operator<<( Out& out, percent_tolerance_t<FPT> t )
+{
+ return out << t.m_value;
+}
+
+//____________________________________________________________________________//
+
+template<typename FPT>
+inline percent_tolerance_t<FPT>
+percent_tolerance( FPT v )
+{
+ return percent_tolerance_t<FPT>( v );
+}
+
+//____________________________________________________________________________//
+
+template<typename FPT>
+struct fraction_tolerance_t {
+ explicit fraction_tolerance_t( FPT v ) : m_value( v ) {}
+
+ FPT m_value;
+};
+
+//____________________________________________________________________________//
+
+template<typename Out,typename FPT>
+Out& operator<<( Out& out, fraction_tolerance_t<FPT> t )
+{
+ return out << t.m_value;
+}
+
+//____________________________________________________________________________//
+
+template<typename FPT>
+inline fraction_tolerance_t<FPT>
+fraction_tolerance( FPT v )
+{
+ return fraction_tolerance_t<FPT>( v );
+}
+
+//____________________________________________________________________________//
+
+// ************************************************************************** //
+// ************** close_at_tolerance ************** //
+// ************************************************************************** //
+
+template<typename FPT>
+class close_at_tolerance {
+public:
+ // Public typedefs
+ typedef bool result_type;
+
+ // Constructor
+ template<typename ToleranceBaseType>
+ explicit close_at_tolerance( percent_tolerance_t<ToleranceBaseType> tolerance,
+ floating_point_comparison_type fpc_type = FPC_STRONG )
+ : p_fraction_tolerance( tt_detail::fpt_abs( static_cast<FPT>(0.01)*tolerance.m_value ) )
+ , p_strong_or_weak( fpc_type == FPC_STRONG )
+ , m_report_modifier( 100. )
+ {}
+ template<typename ToleranceBaseType>
+ explicit close_at_tolerance( fraction_tolerance_t<ToleranceBaseType> tolerance,
+ floating_point_comparison_type fpc_type = FPC_STRONG )
+ : p_fraction_tolerance( tt_detail::fpt_abs( tolerance.m_value ) )
+ , p_strong_or_weak( fpc_type == FPC_STRONG )
+ , m_report_modifier( 1. )
+ {}
+
+ predicate_result operator()( FPT left, FPT right ) const
+ {
+ FPT diff = tt_detail::fpt_abs( left - right );
+ FPT d1 = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( right ) );
+ FPT d2 = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( left ) );
+
+ predicate_result res( p_strong_or_weak
+ ? (d1 <= p_fraction_tolerance.get() && d2 <= p_fraction_tolerance.get())
+ : (d1 <= p_fraction_tolerance.get() || d2 <= p_fraction_tolerance.get()) );
+
+ if( !res )
+ res.message() << (( d1 <= p_fraction_tolerance.get() ? d2 : d1 ) * m_report_modifier);
+
+ return res;
+ }
+
+ // Public properties
+ readonly_property<FPT> p_fraction_tolerance;
+ readonly_property<bool> p_strong_or_weak;
+private:
+ // Data members
+ FPT m_report_modifier;
+};
+
+//____________________________________________________________________________//
+
+// ************************************************************************** //
+// ************** check_is_close ************** //
+// ************************************************************************** //
+
+struct BOOST_TEST_DECL check_is_close_t {
+ // Public typedefs
+ typedef bool result_type;
+
+ template<typename FPT1, typename FPT2, typename ToleranceBaseType>
+ predicate_result
+ operator()( FPT1 left, FPT2 right, percent_tolerance_t<ToleranceBaseType> tolerance,
+ floating_point_comparison_type fpc_type = FPC_STRONG ) const
+ {
+ // deduce "better" type from types of arguments being compared
+ // if one type is floating and the second integral we use floating type and
+ // value of integral type is promoted to the floating. The same for float and double
+ // But we don't want to compare two values of integral types using this tool.
+ typedef typename numeric::conversion_traits<FPT1,FPT2>::supertype FPT;
+ BOOST_STATIC_ASSERT( !is_integral<FPT>::value );
+
+ close_at_tolerance<FPT> pred( tolerance, fpc_type );
+
+ return pred( left, right );
+ }
+ template<typename FPT1, typename FPT2, typename ToleranceBaseType>
+ predicate_result
+ operator()( FPT1 left, FPT2 right, fraction_tolerance_t<ToleranceBaseType> tolerance,
+ floating_point_comparison_type fpc_type = FPC_STRONG ) const
+ {
+ // same as in a comment above
+ typedef typename numeric::conversion_traits<FPT1,FPT2>::supertype FPT;
+ BOOST_STATIC_ASSERT( !is_integral<FPT>::value );
+
+ close_at_tolerance<FPT> pred( tolerance, fpc_type );
+
+ return pred( left, right );
+ }
+};
+
+namespace {
+check_is_close_t const& check_is_close = unit_test::ut_detail::static_constant<check_is_close_t>::value;
+}
+
+//____________________________________________________________________________//
+
+// ************************************************************************** //
+// ************** check_is_small ************** //
+// ************************************************************************** //
+
+struct BOOST_TEST_DECL check_is_small_t {
+ // Public typedefs
+ typedef bool result_type;
+
+ template<typename FPT>
+ bool
+ operator()( FPT fpv, FPT tolerance ) const
+ {
+ return tt_detail::fpt_abs( fpv ) < tt_detail::fpt_abs( tolerance );
+ }
+};
+
+namespace {
+check_is_small_t const& check_is_small = unit_test::ut_detail::static_constant<check_is_small_t>::value;
+}
+
+//____________________________________________________________________________//
+
+} // namespace test_tools
+
+} // namespace boost
+
+//____________________________________________________________________________//
+
+#include <boost/test/detail/enable_warnings.hpp>
+
+#endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER