1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
|
// (C) Copyright Gennadiy Rozental 2001.
// 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
//!@brief algorithms for comparing 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/tools/assertion_result.hpp>
// Boost
#include <boost/limits.hpp> // for std::numeric_limits
#include <boost/static_assert.hpp>
#include <boost/assert.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/is_array.hpp>
#include <boost/type_traits/conditional.hpp>
#include <boost/utility/enable_if.hpp>
// STL
#include <iosfwd>
#include <boost/test/detail/suppress_warnings.hpp>
//____________________________________________________________________________//
namespace boost {
namespace math {
namespace fpc {
// ************************************************************************** //
// ************** fpc::tolerance_based ************** //
// ************************************************************************** //
//! @internal
//! Protects the instanciation of std::numeric_limits from non-supported types (eg. T=array)
template <typename T, bool enabled>
struct tolerance_based_delegate;
template <typename T>
struct tolerance_based_delegate<T, false> : mpl::false_ {};
template <typename T>
struct tolerance_based_delegate<T, true>
: mpl::bool_<
is_floating_point<T>::value ||
(!std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_exact)>
{};
/*!@brief Indicates if a type can be compared using a tolerance scheme
*
* This is a metafunction that should evaluate to @c mpl::true_ if the type
* @c T can be compared using a tolerance based method, typically for floating point
* types.
*
* This metafunction can be specialized further to declare user types that are
* floating point (eg. boost.multiprecision).
*/
template <typename T>
struct tolerance_based : tolerance_based_delegate<T, !is_array<T>::value >::type {};
// ************************************************************************** //
// ************** fpc::strength ************** //
// ************************************************************************** //
//! Method for comparing floating point numbers
enum strength {
FPC_STRONG, //!< "Very close" - equation 2' in docs, the default
FPC_WEAK //!< "Close enough" - equation 3' in docs.
};
// ************************************************************************** //
// ************** tolerance presentation types ************** //
// ************************************************************************** //
template<typename FPT>
struct percent_tolerance_t {
explicit percent_tolerance_t( FPT v ) : m_value( v ) {}
FPT m_value;
};
//____________________________________________________________________________//
template<typename FPT>
inline std::ostream& operator<<( std::ostream& 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 );
}
//____________________________________________________________________________//
// ************************************************************************** //
// ************** details ************** //
// ************************************************************************** //
namespace fpc_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_specialized_limits
{
static FPT min_value() { return (std::numeric_limits<FPT>::min)(); }
static FPT max_value() { return (std::numeric_limits<FPT>::max)(); }
};
template<typename FPT>
struct fpt_non_specialized_limits
{
static FPT min_value() { return static_cast<FPT>(0); }
static FPT max_value() { return static_cast<FPT>(1000000); } // for our purposes it doesn't really matter what value is returned here
};
template<typename FPT>
struct fpt_limits : boost::conditional<std::numeric_limits<FPT>::is_specialized,
fpt_specialized_limits<FPT>,
fpt_non_specialized_limits<FPT>
>::type
{};
//____________________________________________________________________________//
// 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;
}
//____________________________________________________________________________//
template<typename FPT, typename ToleranceType>
inline FPT
fraction_tolerance( ToleranceType tolerance )
{
return static_cast<FPT>(tolerance);
}
//____________________________________________________________________________//
template<typename FPT2, typename FPT>
inline FPT2
fraction_tolerance( percent_tolerance_t<FPT> tolerance )
{
return FPT2(tolerance.m_value)*FPT2(0.01);
}
//____________________________________________________________________________//
} // namespace fpc_detail
// ************************************************************************** //
// ************** close_at_tolerance ************** //
// ************************************************************************** //
/*!@brief Predicate for comparing floating point numbers
*
* This predicate is used to compare floating point numbers. In addition the comparison produces maximum
* related differnce, which can be used to generate detailed error message
* The methods for comparing floating points are detailed in the documentation. The method is chosen
* by the @ref boost::math::fpc::strength given at construction.
*/
template<typename FPT>
class close_at_tolerance {
public:
// Public typedefs
typedef bool result_type;
// Constructor
template<typename ToleranceType>
explicit close_at_tolerance( ToleranceType tolerance, fpc::strength fpc_strength = FPC_STRONG )
: m_fraction_tolerance( fpc_detail::fraction_tolerance<FPT>( tolerance ) )
, m_strength( fpc_strength )
, m_tested_rel_diff( 0 )
{
BOOST_ASSERT_MSG( m_fraction_tolerance >= FPT(0), "tolerance must not be negative!" ); // no reason for tolerance to be negative
}
// Access methods
//! Returns the tolerance
FPT fraction_tolerance() const { return m_fraction_tolerance; }
//! Returns the comparison method
fpc::strength strength() const { return m_strength; }
//! Returns the failing fraction
FPT tested_rel_diff() const { return m_tested_rel_diff; }
/*! Compares two floating point numbers a and b such that their "left" relative difference |a-b|/a and/or
* "right" relative difference |a-b|/b does not exceed specified relative (fraction) tolerance.
*
* @param[in] left first floating point number to be compared
* @param[in] right second floating point number to be compared
*
* What is reported by @c tested_rel_diff in case of failure depends on the comparison method:
* - for @c FPC_STRONG: the max of the two fractions
* - for @c FPC_WEAK: the min of the two fractions
* The rationale behind is to report the tolerance to set in order to make a test pass.
*/
bool operator()( FPT left, FPT right ) const
{
FPT diff = fpc_detail::fpt_abs<FPT>( left - right );
FPT fraction_of_right = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( right ) );
FPT fraction_of_left = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( left ) );
FPT max_rel_diff = (std::max)( fraction_of_left, fraction_of_right );
FPT min_rel_diff = (std::min)( fraction_of_left, fraction_of_right );
m_tested_rel_diff = m_strength == FPC_STRONG ? max_rel_diff : min_rel_diff;
return m_tested_rel_diff <= m_fraction_tolerance;
}
private:
// Data members
FPT m_fraction_tolerance;
fpc::strength m_strength;
mutable FPT m_tested_rel_diff;
};
// ************************************************************************** //
// ************** small_with_tolerance ************** //
// ************************************************************************** //
/*!@brief Predicate for comparing floating point numbers against 0
*
* Serves the same purpose as boost::math::fpc::close_at_tolerance, but used when one
* of the operand is null.
*/
template<typename FPT>
class small_with_tolerance {
public:
// Public typedefs
typedef bool result_type;
// Constructor
explicit small_with_tolerance( FPT tolerance ) // <= absolute tolerance
: m_tolerance( tolerance )
{
BOOST_ASSERT( m_tolerance >= FPT(0) ); // no reason for the tolerance to be negative
}
// Action method
bool operator()( FPT fpv ) const
{
return fpc::fpc_detail::fpt_abs( fpv ) <= m_tolerance;
}
private:
// Data members
FPT m_tolerance;
};
// ************************************************************************** //
// ************** is_small ************** //
// ************************************************************************** //
template<typename FPT>
inline bool
is_small( FPT fpv, FPT tolerance )
{
return small_with_tolerance<FPT>( tolerance )( fpv );
}
//____________________________________________________________________________//
} // namespace fpc
} // namespace math
} // namespace boost
#include <boost/test/detail/enable_warnings.hpp>
#endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER
|