381 lines
9.2 KiB
C++
381 lines
9.2 KiB
C++
//
|
|
//! Copyright (c) 2011
|
|
//! Brandon Kohn
|
|
//
|
|
// 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)
|
|
//
|
|
|
|
#include <boost/operators.hpp>
|
|
#include <boost/numeric/conversion/cast.hpp>
|
|
#include <boost/mpl/for_each.hpp>
|
|
#include <boost/mpl/vector.hpp>
|
|
#include <boost/cstdint.hpp>
|
|
#include <boost/test/minimal.hpp>
|
|
|
|
//! Define a simple custom number
|
|
struct Double
|
|
{
|
|
Double()
|
|
: v(0)
|
|
{}
|
|
|
|
template <typename T>
|
|
explicit Double( T v )
|
|
: v(static_cast<double>(v))
|
|
{}
|
|
|
|
template <typename T>
|
|
Double& operator= ( T t )
|
|
{
|
|
v = static_cast<double>(t);
|
|
return *this;
|
|
}
|
|
|
|
bool operator < ( const Double& rhs ) const
|
|
{
|
|
return v < rhs.v;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator < ( T rhs ) const
|
|
{
|
|
return v < static_cast<double>(rhs);
|
|
}
|
|
|
|
template <typename LHS>
|
|
friend bool operator < ( const LHS& lhs, const Double& rhs )
|
|
{
|
|
return lhs < rhs.v;
|
|
}
|
|
|
|
bool operator > ( const Double& rhs ) const
|
|
{
|
|
return v > rhs.v;
|
|
}
|
|
|
|
template <typename LHS>
|
|
friend bool operator > ( const LHS& lhs, const Double& rhs )
|
|
{
|
|
return lhs > rhs.v;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator > ( T rhs ) const
|
|
{
|
|
return v > static_cast<double>(rhs);
|
|
}
|
|
|
|
bool operator == ( const Double& rhs ) const
|
|
{
|
|
return v == rhs.v;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator == ( T rhs ) const
|
|
{
|
|
return v == static_cast<double>(rhs);
|
|
}
|
|
|
|
template <typename LHS>
|
|
friend bool operator == ( const LHS& lhs, const Double& rhs )
|
|
{
|
|
return lhs == rhs.v;
|
|
}
|
|
|
|
bool operator !() const
|
|
{
|
|
return v == 0;
|
|
}
|
|
|
|
Double operator -() const
|
|
{
|
|
return Double(-v);
|
|
}
|
|
|
|
Double& operator +=( const Double& t )
|
|
{
|
|
v += t.v;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
Double& operator +=( T t )
|
|
{
|
|
v += static_cast<double>(t);
|
|
return *this;
|
|
}
|
|
|
|
Double& operator -=( const Double& t )
|
|
{
|
|
v -= t.v;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
Double& operator -=( T t )
|
|
{
|
|
v -= static_cast<double>(t);
|
|
return *this;
|
|
}
|
|
|
|
Double& operator *= ( const Double& factor )
|
|
{
|
|
v *= factor.v;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
Double& operator *=( T t )
|
|
{
|
|
v *= static_cast<double>(t);
|
|
return *this;
|
|
}
|
|
|
|
Double& operator /= (const Double& divisor)
|
|
{
|
|
v /= divisor.v;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
Double& operator /=( T t )
|
|
{
|
|
v /= static_cast<double>(t);
|
|
return (*this);
|
|
}
|
|
|
|
double v;
|
|
};
|
|
|
|
//! Define numeric_limits for the custom type.
|
|
namespace std
|
|
{
|
|
template<>
|
|
class numeric_limits< Double > : public numeric_limits<double>
|
|
{
|
|
public:
|
|
|
|
//! Limit our Double to a range of +/- 100.0
|
|
static Double (min)()
|
|
{
|
|
return Double(1.e-2);
|
|
}
|
|
|
|
static Double (max)()
|
|
{
|
|
return Double(1.e2);
|
|
}
|
|
|
|
static Double epsilon()
|
|
{
|
|
return Double( std::numeric_limits<double>::epsilon() );
|
|
}
|
|
};
|
|
}
|
|
|
|
//! Define range checking and overflow policies.
|
|
namespace custom
|
|
{
|
|
//! Define a custom range checker
|
|
template<typename Traits, typename OverFlowHandler>
|
|
struct range_checker
|
|
{
|
|
typedef typename Traits::argument_type argument_type ;
|
|
typedef typename Traits::source_type S;
|
|
typedef typename Traits::target_type T;
|
|
|
|
//! Check range of integral types.
|
|
static boost::numeric::range_check_result out_of_range( argument_type s )
|
|
{
|
|
using namespace boost::numeric;
|
|
if( s > bounds<T>::highest() )
|
|
return cPosOverflow;
|
|
else if( s < bounds<T>::lowest() )
|
|
return cNegOverflow;
|
|
else
|
|
return cInRange;
|
|
}
|
|
|
|
static void validate_range ( argument_type s )
|
|
{
|
|
BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_bounded );
|
|
OverFlowHandler()( out_of_range(s) );
|
|
}
|
|
};
|
|
|
|
//! Overflow handler
|
|
struct positive_overflow{};
|
|
struct negative_overflow{};
|
|
|
|
struct overflow_handler
|
|
{
|
|
void operator() ( boost::numeric::range_check_result r )
|
|
{
|
|
using namespace boost::numeric;
|
|
if( r == cNegOverflow )
|
|
throw negative_overflow() ;
|
|
else if( r == cPosOverflow )
|
|
throw positive_overflow() ;
|
|
}
|
|
};
|
|
|
|
//! Define a rounding policy and specialize on the custom type.
|
|
template<class S>
|
|
struct Ceil : boost::numeric::Ceil<S>{};
|
|
|
|
template<>
|
|
struct Ceil<Double>
|
|
{
|
|
typedef Double source_type;
|
|
|
|
typedef Double const& argument_type;
|
|
|
|
static source_type nearbyint ( argument_type s )
|
|
{
|
|
#if !defined(BOOST_NO_STDC_NAMESPACE)
|
|
using std::ceil ;
|
|
#endif
|
|
return Double( ceil(s.v) );
|
|
}
|
|
|
|
typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_infinity> round_style;
|
|
};
|
|
|
|
//! Define a rounding policy and specialize on the custom type.
|
|
template<class S>
|
|
struct Trunc: boost::numeric::Trunc<S>{};
|
|
|
|
template<>
|
|
struct Trunc<Double>
|
|
{
|
|
typedef Double source_type;
|
|
|
|
typedef Double const& argument_type;
|
|
|
|
static source_type nearbyint ( argument_type s )
|
|
{
|
|
#if !defined(BOOST_NO_STDC_NAMESPACE)
|
|
using std::floor;
|
|
#endif
|
|
return Double( floor(s.v) );
|
|
}
|
|
|
|
typedef boost::mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style;
|
|
};
|
|
}//namespace custom;
|
|
|
|
namespace boost { namespace numeric {
|
|
|
|
//! Define the numeric_cast_traits specializations on the custom type.
|
|
template <typename S>
|
|
struct numeric_cast_traits<Double, S>
|
|
{
|
|
typedef custom::overflow_handler overflow_policy;
|
|
typedef custom::range_checker
|
|
<
|
|
boost::numeric::conversion_traits<Double, S>
|
|
, overflow_policy
|
|
> range_checking_policy;
|
|
typedef boost::numeric::Trunc<S> rounding_policy;
|
|
};
|
|
|
|
template <typename T>
|
|
struct numeric_cast_traits<T, Double>
|
|
{
|
|
typedef custom::overflow_handler overflow_policy;
|
|
typedef custom::range_checker
|
|
<
|
|
boost::numeric::conversion_traits<T, Double>
|
|
, overflow_policy
|
|
> range_checking_policy;
|
|
typedef custom::Trunc<Double> rounding_policy;
|
|
};
|
|
|
|
//! Define the conversion from the custom type to built-in types and vice-versa.
|
|
template<typename T>
|
|
struct raw_converter< conversion_traits< T, Double > >
|
|
{
|
|
static T low_level_convert ( const Double& n )
|
|
{
|
|
return static_cast<T>( n.v );
|
|
}
|
|
};
|
|
|
|
template<typename S>
|
|
struct raw_converter< conversion_traits< Double, S > >
|
|
{
|
|
static Double low_level_convert ( const S& n )
|
|
{
|
|
return Double(n);
|
|
}
|
|
};
|
|
}}//namespace boost::numeric;
|
|
|
|
#define BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( CastCode ) \
|
|
try { CastCode; BOOST_CHECK( false ); } \
|
|
catch( custom::positive_overflow& ){} \
|
|
catch(...){ BOOST_CHECK( false ); } \
|
|
/***/
|
|
|
|
#define BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( CastCode ) \
|
|
try { CastCode; BOOST_CHECK( false ); } \
|
|
catch( custom::negative_overflow& ){} \
|
|
catch(...){ BOOST_CHECK( false ); } \
|
|
/***/
|
|
|
|
struct test_cast_traits
|
|
{
|
|
template <typename T>
|
|
void operator()(T) const
|
|
{
|
|
Double d = boost::numeric_cast<Double>( static_cast<T>(50) );
|
|
BOOST_CHECK( d.v == 50. );
|
|
T v = boost::numeric_cast<T>( d );
|
|
BOOST_CHECK( v == 50 );
|
|
}
|
|
};
|
|
|
|
void test_numeric_cast_traits()
|
|
{
|
|
typedef boost::mpl::vector
|
|
<
|
|
boost::int8_t
|
|
, boost::uint8_t
|
|
, boost::int16_t
|
|
, boost::uint16_t
|
|
, boost::int32_t
|
|
, boost::uint32_t
|
|
#if !defined( BOOST_NO_INT64_T )
|
|
, boost::int64_t
|
|
, boost::uint64_t
|
|
#endif
|
|
, float
|
|
, double
|
|
, long double
|
|
> types;
|
|
boost::mpl::for_each<types>( test_cast_traits() );
|
|
|
|
//! Check overflow handler.
|
|
Double d( 56.0 );
|
|
BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW( d = boost::numeric_cast<Double>( 101 ) );
|
|
BOOST_CHECK( d.v == 56. );
|
|
BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW( d = boost::numeric_cast<Double>( -101 ) );
|
|
BOOST_CHECK( d.v == 56.);
|
|
|
|
//! Check custom round policy.
|
|
d = 5.9;
|
|
int five = boost::numeric_cast<int>( d );
|
|
BOOST_CHECK( five == 5 );
|
|
}
|
|
|
|
int test_main( int argc, char * argv[] )
|
|
{
|
|
test_numeric_cast_traits();
|
|
return 0;
|
|
}
|
|
|
|
#undef BOOST_TEST_CATCH_CUSTOM_POSITIVE_OVERFLOW
|
|
#undef BOOST_TEST_CATCH_CUSTOM_NEGATIVE_OVERFLOW
|