From 7af42f27653e5fb71c3d087a44bfb58cb49b5070 Mon Sep 17 00:00:00 2001 From: Robert Ramey Date: Wed, 4 Nov 2015 21:26:07 -0800 Subject: [PATCH] resolved division --- include/automatic.hpp | 82 ++- include/checked.hpp | 959 ++++----------------------- include/concept/promotion_policy.hpp | 7 + include/interval.hpp | 90 +-- include/native.hpp | 21 + include/safe_base_operations.hpp | 55 +- test/test_checked_divide.cpp | 7 +- test/test_divide.cpp | 1 + test/test_divide_auto.cpp | 1 + 9 files changed, 273 insertions(+), 950 deletions(-) diff --git a/include/automatic.hpp b/include/automatic.hpp index 01416c0..b4e19dc 100644 --- a/include/automatic.hpp +++ b/include/automatic.hpp @@ -30,8 +30,18 @@ namespace boost { namespace numeric { +namespace checked { +template + checked_result + SAFE_NUMERIC_CONSTEXPR divide( + const T & t, + const U & u + ); +} // checked + struct automatic { +/* template using calculate_max_t2 = typename boost::mpl::if_c< @@ -40,7 +50,7 @@ struct automatic { std::intmax_t, std::uintmax_t >::type; - +*/ // section 4.13 integer conversion rank template using rank = @@ -126,6 +136,7 @@ struct automatic { defer_unsigned_lazily >::type; + /////////////////////////////////////////////////////////////////////// template struct addition_result { typedef typename base_type::type base_type_t; @@ -192,6 +203,8 @@ struct automatic { >::type p_safe_range; */ }; + + /////////////////////////////////////////////////////////////////////// template struct subtraction_result { typedef typename base_type::type base_type_t; @@ -246,6 +259,7 @@ struct automatic { >::type type; }; + /////////////////////////////////////////////////////////////////////// template struct multiplication_result { typedef typename base_type::type base_type_t; @@ -300,6 +314,8 @@ struct automatic { E >::type type; }; + + /////////////////////////////////////////////////////////////////////// template struct division_result { typedef typename base_type::type base_type_t; @@ -325,21 +341,7 @@ struct automatic { base_value(std::numeric_limits::max()) ); }; - /* - SAFE_NUMERIC_CONSTEXPR static const interval t = { - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; - SAFE_NUMERIC_CONSTEXPR static const interval u = { - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; - */ - /* - typedef calculate_max_t max_t; - //typedef print p_max_t; - */ using max_t = typename boost::mpl::if_c< std::numeric_limits::is_signed || std::numeric_limits::is_signed, @@ -451,25 +453,45 @@ struct automatic { >::type type; }; + // forward to correct divide implementation + template + checked_result + static SAFE_NUMERIC_CONSTEXPR divide( + const T & t, + const U & u + ){ + return checked::divide_automatic(t, u); + } + + /////////////////////////////////////////////////////////////////////// template struct modulus_result { - typedef typename base_type::type t_base_type; - typedef typename base_type::type u_base_type; - SAFE_NUMERIC_CONSTEXPR static const interval t = { - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; - SAFE_NUMERIC_CONSTEXPR static const interval u = { + typedef typename base_type::type base_type_t; + static_assert( + std::is_literal_type< interval >::value, + "interval is not literal type" + ); + typedef typename base_type::type base_type_u; + static_assert( + std::is_literal_type< interval >::value, + "interval is not tliteral type" + ); + using max_t = typename boost::mpl::if_c< + std::numeric_limits::is_signed + || std::numeric_limits::is_signed, + std::intmax_t, + std::uintmax_t + >::type; + + typedef typename safe_range< + max_t, base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; - typedef decltype(t_base_type() % u_base_type()) r_base_type; - - SAFE_NUMERIC_CONSTEXPR static const interval r - = operator%(t, u); - - typedef safe_base type; + base_value(std::numeric_limits::max()), + P, + E + >::type type; }; + template struct left_shift_result { typedef typename base_type::type t_base_type; diff --git a/include/checked.hpp b/include/checked.hpp index 59156e1..323138f 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -18,12 +18,9 @@ #include #include // is_integral, make_unsigned #include // std::max -#include // std::int_fastN_t #include -#include -#include -#include +#include #include "safe_common.hpp" #include "checked_result.hpp" @@ -33,7 +30,7 @@ namespace numeric { namespace checked { //////////////////////////////////////////////////// -// layer 0 - implment safe operations for intrinsic integers +// layer 0 - implement safe operations for intrinsic integers // Note presumption of twos complement integer arithmetic //////////////////////////////////////////////////// @@ -108,13 +105,11 @@ namespace detail { checked_result >::type SAFE_NUMERIC_CONSTEXPR add( - const R & minr, - const R & maxr, const R t, const R u ) { return - maxr - u < t ? + std::numeric_limits::max() - u < t ? checked_result( exception_type::overflow_error, "addition overflow" @@ -131,14 +126,12 @@ namespace detail { checked_result >::type SAFE_NUMERIC_CONSTEXPR add( - const R & minr, - const R & maxr, const R t, const R u ) { return - ((u > 0) && (t > (maxr - u))) - || ((u < 0) && (t < (minr - u))) ? + ((u > 0) && (t > (std::numeric_limits::max() - u))) + || ((u < 0) && (t < (std::numeric_limits::min() - u))) ? checked_result( exception_type::overflow_error, "addition overflow" @@ -152,8 +145,6 @@ namespace detail { template SAFE_NUMERIC_CONSTEXPR checked_result add( - const R & minr, - const R & maxr, const T & t, const U & u ) { @@ -168,22 +159,7 @@ SAFE_NUMERIC_CONSTEXPR checked_result add( ru != exception_type::no_exception ? ru : - detail::add(minr, maxr, t, u) - ; -} - -template -SAFE_NUMERIC_CONSTEXPR checked_result add( - const T & t, - const U & u -) { - return - add( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) + detail::add(t, u) ; } @@ -198,8 +174,6 @@ typename boost::enable_if< checked_result >::type SAFE_NUMERIC_CONSTEXPR subtract( - const R & minr, - const R & maxr, const R t, const R u ) { @@ -222,14 +196,12 @@ typename boost::enable_if< checked_result >::type SAFE_NUMERIC_CONSTEXPR subtract( - const R & minr, - const R & maxr, const R t, const R u ) { // INT32-C return - ((u > 0) && (t < (minr + u))) - || ((u < 0) && (t > (maxr + u))) ? + ((u > 0) && (t < (std::numeric_limits::min() + u))) + || ((u < 0) && (t > (std::numeric_limits::max() + u))) ? checked_result( exception_type::overflow_error, "subtraction overflow" @@ -243,8 +215,6 @@ SAFE_NUMERIC_CONSTEXPR subtract( template SAFE_NUMERIC_CONSTEXPR checked_result subtract( - const R & minr, - const R & maxr, const T & t, const U & u ) { @@ -257,22 +227,7 @@ SAFE_NUMERIC_CONSTEXPR checked_result subtract( cast(u) != exception_type::no_exception ? cast(u) : - detail::subtract(minr, maxr, t, u) - ; -} - -template -SAFE_NUMERIC_CONSTEXPR checked_result subtract( - const T & t, - const U & u -) { - return - subtract( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) + detail::subtract(t, u) ; } @@ -288,8 +243,6 @@ typename boost::enable_if_c< checked_result >::type SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, const R t, const R u ) { @@ -314,8 +267,6 @@ typename boost::enable_if_c< checked_result >::type SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, const R t, const R u ) { @@ -338,8 +289,6 @@ typename boost::enable_if_c< checked_result >::type SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, const R t, const R u ) { @@ -375,8 +324,6 @@ typename boost::enable_if_c< checked_result >::type SAFE_NUMERIC_CONSTEXPR multiply( - const R & minr, - const R & maxr, const R t, const R u ) { // INT32-C @@ -421,8 +368,6 @@ SAFE_NUMERIC_CONSTEXPR multiply( template SAFE_NUMERIC_CONSTEXPR checked_result multiply( - const R & minr, - const R & maxr, const T & t, const U & u ) { @@ -440,21 +385,7 @@ SAFE_NUMERIC_CONSTEXPR checked_result multiply( ? checked_result(static_cast(t) * static_cast(u)) : - detail::multiply(minr, maxr, t, u) - ; -} -template -SAFE_NUMERIC_CONSTEXPR checked_result multiply( - const T & t, - const U & u -) { - return - multiply( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) + detail::multiply(t, u) ; } @@ -464,38 +395,23 @@ SAFE_NUMERIC_CONSTEXPR checked_result multiply( namespace detail { template - checked_result - SAFE_NUMERIC_CONSTEXPR divide_limit( - const R & minr, - const R & maxr, + typename boost::enable_if_c< + !std::numeric_limits::is_signed, + checked_result + >::type + SAFE_NUMERIC_CONSTEXPR divide( const R & t, const R & u ){ - const R r = t / u; - return - (r > maxr) ? - checked_result( - exception_type::overflow_error, - "division resulted in overflow" - ) - : - (r < minr) ? - checked_result( - exception_type::underflow_error, - "division resulted in underflow" - ) - : - checked_result(r) - ; + return t / u; } + template typename boost::enable_if_c< std::numeric_limits::is_signed, checked_result >::type SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, const R & t, const R & u ){ @@ -506,554 +422,9 @@ namespace detail { "result cannot be represented" ) : - divide_limit(minr, maxr, t, u) - ; - } - template - typename boost::enable_if_c< - !std::numeric_limits::is_signed, - checked_result - >::type - SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const R & t, - const R & u - ){ - return divide_limit(minr, maxr, t, u); - } -} - -template -checked_result -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - if(u == 0){ - return checked_result( - exception_type::domain_error, - "divide by zero" - ); - } - - auto tx = cast(t); - auto ux = cast(u); - if(!tx.no_exception() - || !ux.no_exception()) - return checked_result( - exception_type::overflow_error, - "failure converting argument types" - ); - return - detail::divide( - minr, - maxr, - tx.m_r, - ux.m_r - ) - ; -} - -template -checked_result -SAFE_NUMERIC_CONSTEXPR divide( - const T & t, - const U & u -){ - return - divide( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) - ; -} - -#if 1 -namespace detail2 { - -template -checked_result -divide_limits( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - auto x = t / u; - const R r = x; - //const R r = t / u; - return - (r > maxr) ? - checked_result( - exception_type::overflow_error, - "division resulted in overflow" - ) - : - (r < minr) ? - checked_result( - exception_type::underflow_error, - "division resulted in underflow" - ) - : - checked_result(r) - ; -} - -template -typename boost::enable_if_c< - ! std::is_signed::value && ! std::is_signed::value, - checked_result ->::type -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - // comment this out as a reminder not to assume that - // unsigned op unsigned -> unsigned is always true - //static_assert(! std::is_signed::value, "unexpected type for result"); - return divide_limits(minr, maxr, t, u); -} - -template -struct fast_int_t { - typedef boost::mpl::vector< - std::int_fast8_t, - std::int_fast16_t, - std::int_fast32_t, - std::int_fast64_t, - std::int_fast64_t - > int_types; - - typedef typename boost::mpl::at_c::type type; -}; - -template -constexpr static int bits(){ - // n is number of bits including sign - const int n = std::max( - std::numeric_limits::digits, - std::numeric_limits::digits - ) - + 1 // one guard bit to cover u == -1 & t = numeric_limits::min() - + 1; // one sign bit - return - (n <= 8) ? - 0 - : (n <= 16) ? - 1 - : (n <= 32) ? - 2 - : (n <= 64) ? - 3 - : 4 - ; -} - -template -using temp_integer = typename fast_int_t()>::type; - -template -typename boost::enable_if_c< - std::is_signed::value && std::is_signed::value, - checked_result ->::type -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - using t_type = temp_integer; - if(std::is_same::value){ - if(u == -1 && t == std::numeric_limits::min()) - return - checked_result( - exception_type::domain_error, - "result cannot be represented" - ) - ; - } - return divide_limits( - minr, - maxr, - cast(t), - cast(u) - ); -} - -template -typename boost::enable_if_c< - ! std::is_signed::value && std::is_signed::value, - checked_result ->::type -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - using t_type = temp_integer; - return divide_limits( - minr, - maxr, - cast(t), - cast(u) - ); -} - -template -typename boost::enable_if_c< - std::is_signed::value && ! std::is_signed::value, - checked_result ->::type -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - return divide_limits( - minr, - maxr, - t, - u - ); -} - -} // detail2 - -template -checked_result -SAFE_NUMERIC_CONSTEXPR divide2( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - if(u == 0){ - return checked_result( - exception_type::domain_error, - "divide by zero" - ); - } - return - detail2::divide( - minr, - maxr, - t, - u - ) - ; - -} - -template -checked_result -SAFE_NUMERIC_CONSTEXPR divide2( - const T & t, - const U & u -){ - return - divide( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) - ; -} -#endif - -#if 0 - - checked_result(t / u) // > 0 - : - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - // (u < 0) - (u > -t) ? - checked_result(0) - : - //(u <= -t) ? - checked_result(t / u) // < 0 - : // u unsigned - checked_result( - exception_type::static_assertion, - "impossible to arrive here" - ) - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : // u > 0 - checked_result(t / u) - ; -} -template -checked_result -SAFE_NUMERIC_CONSTEXPR divide( - const T & t, - const U & u -){ - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - (t >= 0) ? - (u > t) ? - checked_result(0) - : - (u > 0) ? - checked_result(t / u) // > 0 - : - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - // (u < 0) - (std::numeric_limits::is_signed) ? - (u > -t) ? - checked_result(0) - : - //(u <= -t) ? - checked_result(t / u) // < 0 - : // u unsigned - checked_result( - exception_type::static_assertion, - "impossible to arrive here" - ) - : (t >= std::numeric_limits::min() / 2) ? - (std::numeric_limits::is_signed) ? - (u < t) ? - checked_result(0) - : - (u < -1) ? - (t == std::numeric_limits::min()) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - checked_result(t / u) // > 0 - : - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - // (u > 0) - checked_result(-(-t / u)) - checked_result(static_cast(t)) - : - // u not signed - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - //(u > 0) - checked_result(-(-t / u)) - ; -} - - - (u < t) ? - checked_result(0) - : - (u < -1) ? - checked_result(t / u) // > 0 - : - // only can happen with twos complement machines - (u == -1) - && t == std::numeric_limits::min() ? - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : // u > 0 checked_result(t / u) - -template -checked_result -SAFE_NUMERIC_CONSTEXPR ugt0( - const T & t, - const U & u -){ - //typedef print > p_u; - return checked_result(t / u); -} - -template -checked_result -SAFE_NUMERIC_CONSTEXPR divide( - const T & t, - const U & u -){ - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - safe_compare::greater_than(t, 0) ? // (t > 0) ? - safe_compare::greater_than(u, t) ? // (u > t) ? - checked_result(0) - : - safe_compare::greater_than(u, 0) ? // (u > 0) ? - checked_result(t / u) // > 0 - : - safe_compare::equal(u, 0) ? // (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - // (u < 0) - (std::numeric_limits::is_signed) ? - safe_compare::greater_than(u, -t) ? //(u > -t) ? - checked_result(0) - : - //(u <= -t) ? - checked_result(t / u) // < 0 - : - checked_result( - exception_type::static_assertion, - "impossible to arrive here" - ) - : // (t <= 0) - (std::numeric_limits::is_signed) ? - safe_compare::less_than(u, t) ? // (u < t) ? - checked_result(0) - : - safe_compare::less_than(u, -1) ? // (u < -1) ? - checked_result(t / u) // > 0 - : - // only can happen with twos complement machines - safe_compare::equal(u, -1) - && safe_compare::equal(t, std::numeric_limits::min()) ? - //(u == -1 && t == std::numeric_limits::min()) ? - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - safe_compare::equal(u, 0) ? // (u == 0) - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : // u > 0 - checked_result(checked::cast(t) / u) - : - // u not signed - // (u <= -t) <=> -u >= t - safe_compare::greater_than(-u, t) ? - checked_result(t / u) - : - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) ; - //: // u > -t <=> -u < t - // checked_result(0); - // not cast so that something like - // (signed_char)(-128) doesn't get transformed to +128 which - // is not representable by a signed char. Here it will get - // transformed to a type (R)(+128). At compile time R is - // std::intmax_t which will hold +128 no problem. - // At runtime - R will be some value. If R can't hold -t then - // we want to invoke a runtime trap so let it fail. - //checked_result(static_cast(t) / u) - //ugt0(t, u -} - -namespace detail { - -template -checked_result -SAFE_NUMERIC_CONSTEXPR divide( - const R & t, - const R & u -){ - return - (t > 0) ? - (u > t) ? - checked_result(0) - : - (u > 0) ? - checked_result(t / u) // > 0 - : - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - (std::numeric_limits::is_signed) ? - (u > -t) ? - checked_result(0) - : - //(u <= -t) ? - checked_result(t / u) // < 0 - : - checked_result( - exception_type::static_assertion, - "impossible to arrive here" - ) - : // (t <= 0) - (u < t) ? - checked_result(0) - : - (u < -1) ? - checked_result(t / u) // > 0 - : - // only can happen with twos complement machines - (u == -1 && t == std::numeric_limits::min()) ? - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - // (u <= -t) - (-u > t) ? - std::numeric_limits::is_signed ? - checked_result(t / u) // < 0 - : - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - // u > -t - checked_result(0) - ; -} - + } } // detail template @@ -1062,179 +433,95 @@ SAFE_NUMERIC_CONSTEXPR divide( const T & t, const U & u ){ - const checked_result rt = cast(t); - if(! rt.no_exception()) - return checked_result(rt.m_e, rt.m_msg); - - const checked_result ru = cast(u); - if(! ru.no_exception()) - return checked_result(ru.m_e, ru.m_msg); - return - detail::divide( - static_cast(rt), - static_cast(ru) + if(u == 0){ + return checked_result( + exception_type::domain_error, + "divide by zero" ); + } + auto tx = cast(t); + auto ux = cast(u); + if(!tx.no_exception() + || !ux.no_exception()) + return checked_result( + exception_type::overflow_error, + "failure converting argument types" + ); + return detail::divide(tx.m_r, ux.m_r); } +namespace detail_automatic { + + template + constexpr static int bits(){ + // n is number of bits including sign + return std::min( + std::max( + std::numeric_limits::digits, + std::numeric_limits::digits + ) + + 1 // one guard bit to cover u == -1 & t = numeric_limits::min() + + 1 // one sign bit + , + std::numeric_limits::digits + + 1 // one sign bit + ); + } + + template + typename boost::enable_if_c< + !std::numeric_limits::is_signed, + checked_result + >::type + SAFE_NUMERIC_CONSTEXPR divide( + const T & t, + const U & u + ){ + return t / u; + } + + template + typename boost::enable_if_c< + std::numeric_limits::is_signed, + checked_result + >::type + SAFE_NUMERIC_CONSTEXPR divide( + const T & t, + const U & u + ){ + using t_type = typename boost::int_t()>::fast; + if(std::numeric_limits::digits == std::numeric_limits::digits){ + if(u == -1 && t == std::numeric_limits::min()) + return + checked_result( + exception_type::domain_error, + "result cannot be represented" + ) + ; + } + return cast(t) / cast(u); + } + +} // detail_automatic + template checked_result -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, +SAFE_NUMERIC_CONSTEXPR divide_automatic( const T & t, const U & u ){ - return - (t > 0) ? - safe_compare::greater_than(u,t) ? // u > t - checked_result(0) - : - safe_compare::greater_than(u,0) ? // u > 0 - checked_result(t / u) // > 0 - : - safe_compare::equal(u, 0) ? // u == 0 - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - safe_compare::less_than_equal(-u, t) // u >= -t ie -u <= t - && (! std::numeric_limits::is_signed) ? - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - //(u < -t) ? - checked_result(0) - : // (t <= 0) - safe_compare::less_than(u,t) ? // u < t - checked_result(0) - : - safe_compare::less_than(u,-1) ? // u < -1 - checked_result(t / u) // > 0 - : - safe_compare::equal(u, -1) - && safe_compare::equal(t, std::numeric_limits::min()) ? - //(u == -1 && t == std::numeric_limits::min()) ? - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - safe_compare::equal(u,0) ? // u == 0 - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - // (u <= -t) - safe_compare::greater_than(-u, t) ? - // (-u > t) ? - std::numeric_limits::is_signed ? - checked_result(t / u) // < 0 - : - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - // u > -t - checked_result(0) - ; + if(u == 0){ + return checked_result( + exception_type::domain_error, + "divide by zero" + ); + } + return detail_automatic::divide(t, u); } -namespace detail { - -template -typename boost::enable_if_c< - ! std::is_signed::value || ! std::is_signed::value, - checked_result ->::type -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -) { - return checked_result(t / u); -} - -template -typename boost::enable_if_c< - std::is_signed::value && std::is_signed::value, - checked_result ->::type -SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -){ - return - (u == -1 && t == minr) ? // special case - could overflow !!! - checked_result( - exception_type::domain_error, - "result unrepresentable" - ) - : - checked_result(t / u); - ; -} - -} // namespace detail - -template -SAFE_NUMERIC_CONSTEXPR checked_result divide( - const R & minr, - const R & maxr, - const T & t, - const U & u -) { - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - static_assert(std::is_integral::value, "only intrinsic integers permitted"); - // typedef print> p_t; - // typedef print > p_u; - return - cast(t) != exception_type::no_exception ? - cast(t) - : - // cast(u) != exception_type::no_exception ? - // cast(u) - // : - (u == 0) ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - detail::divide(minr, maxr, t, u) - ; - ; -} - -/* -template -SAFE_NUMERIC_CONSTEXPR checked_result divide( - const T & t, - const U & u -) { - return - divide( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) - ; -} -*/ -#endif - //////////////////////////////// // safe modulus on unsafe types -/* namespace detail { template @@ -1243,8 +530,6 @@ typename boost::enable_if_c< checked_result >::type SAFE_NUMERIC_CONSTEXPR modulus( - const R & minr, - const R & maxr, const R t, const R u ) { @@ -1257,8 +542,6 @@ typename boost::enable_if_c< checked_result >::type SAFE_NUMERIC_CONSTEXPR modulus( - const R & minr, - const R & maxr, const R t, const R u ){ @@ -1277,45 +560,33 @@ SAFE_NUMERIC_CONSTEXPR modulus( } // namespace detail template -SAFE_NUMERIC_CONSTEXPR checked_result modulus( - const R & minr, - const R & maxr, +checked_result +SAFE_NUMERIC_CONSTEXPR modulus( const T & t, const U & u ) { static_assert(std::is_integral::value, "only intrinsic integers permitted"); static_assert(std::is_integral::value, "only intrinsic integers permitted"); - return - cast(t) != exception_type::no_exception ? - cast(t) - : - cast(u) != exception_type::no_exception ? - cast(u) - : - u == 0 ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - detail::divide(minr, maxr, t, u) - ; + if(0 == u) + return checked_result( + exception_type::domain_error, + "denominator is zero" + ); + + checked_result rt = cast(t); + if(cast(t) != exception_type::no_exception) + return rt; + + checked_result ru = cast(u); + if(cast(u) != exception_type::no_exception) + return ru; + + return detail::modulus( + static_cast(rt), + static_cast(ru) + ); } -template -SAFE_NUMERIC_CONSTEXPR checked_result modulus( - const T & t, - const U & u -) { - return - divide( - std::numeric_limits::min(), - std::numeric_limits::max(), - t, - u - ) - ; -} -*/ + /////////////////////////////////// // shift operations diff --git a/include/concept/promotion_policy.hpp b/include/concept/promotion_policy.hpp index 3295318..fdd983d 100644 --- a/include/concept/promotion_policy.hpp +++ b/include/concept/promotion_policy.hpp @@ -12,6 +12,8 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#include "checked_result.hpp" + namespace boost { namespace numeric { @@ -27,6 +29,11 @@ struct PromotionPolicy { typedef typename PP::template modulus_result mod_type; typedef typename PP::template left_shift_result ls_type; typedef typename PP::template right_shift_result rs_type; + + checked_result::type> test(){ + return PP::template divide::type>(0, 0); + } + }; } // numeric diff --git a/include/interval.hpp b/include/interval.hpp index 6f79c80..dd0671e 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -16,15 +16,47 @@ #include #include + #include #include "checked_result.hpp" #include "checked.hpp" +// from stack overflow +// http://stackoverflow.com/questions/23815138/implementing-variadic-min-max-functions + namespace boost { namespace numeric { +template +T&& vmin(T&& val) +{ + return std::forward(val); +} + +template +auto vmin(T0&& val1, T1&& val2, Ts&&... vs) +{ + return (val1 < val2) ? + vmin(val1, std::forward(vs)...) : + vmin(val2, std::forward(vs)...); +} + +template +T&& vmax(T&& val) +{ + return std::forward(val); +} + +template +auto vmax(T0&& val1, T1&& val2, Ts&&... vs) +{ + return (val1 > val2) ? + vmax(val1, std::forward(vs)...) : + vmax(val2, std::forward(vs)...); +} + template struct interval { static_assert( @@ -105,7 +137,7 @@ struct interval { // other inteval t template SAFE_NUMERIC_CONSTEXPR bool includes(const interval & t) const { - // not very tricky algebra here. the <= and >= operators + // note very tricky algebra here. the <= and >= operators // on checked_result yield tribool. If either argument is an exception // condition, he result is indeterminate. The result of && on two // tribools is indeterminant if either is indeterminate. @@ -136,25 +168,17 @@ SAFE_NUMERIC_CONSTEXPR interval operator*(const interval & t, const interv // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic return interval( - min( - min( - checked::multiply(static_cast(t.l), static_cast(u.l)), - checked::multiply(static_cast(t.l), static_cast(u.u)) - ), - min( - checked::multiply(static_cast(t.u), static_cast(u.l)), - checked::multiply(static_cast(t.u), static_cast(u.u)) - ) + vmin( + checked::multiply(static_cast(t.l), static_cast(u.l)), + checked::multiply(static_cast(t.l), static_cast(u.u)), + checked::multiply(static_cast(t.u), static_cast(u.l)), + checked::multiply(static_cast(t.u), static_cast(u.u)) ), - max( - max( - checked::multiply(static_cast(t.l), static_cast(u.l)), - checked::multiply(static_cast(t.l), static_cast(u.u)) - ), - max( - checked::multiply(static_cast(t.u), static_cast(u.l)), - checked::multiply(static_cast(t.u), static_cast(u.u)) - ) + vmin( + checked::multiply(static_cast(t.l), static_cast(u.l)), + checked::multiply(static_cast(t.l), static_cast(u.u)), + checked::multiply(static_cast(t.u), static_cast(u.l)), + checked::multiply(static_cast(t.u), static_cast(u.u)) ) ); } @@ -176,25 +200,17 @@ SAFE_NUMERIC_CONSTEXPR inline interval operator/( ) : interval( - min( - min( - checked::divide(static_cast(t.l), static_cast(u.l)), - checked::divide(static_cast(t.l), static_cast(u.u)) - ), - min( - checked::divide(static_cast(t.u), static_cast(u.l)), - checked::divide(static_cast(t.u), static_cast(u.u)) - ) + vmin( + checked::divide(static_cast(t.l), static_cast(u.l)), + checked::divide(static_cast(t.l), static_cast(u.u)), + checked::divide(static_cast(t.u), static_cast(u.l)), + checked::divide(static_cast(t.u), static_cast(u.u)) ), - max( - max( - checked::divide(static_cast(t.l), static_cast(u.l)), - checked::divide(static_cast(t.l), static_cast(u.u)) - ), - max( - checked::divide(static_cast(t.u), static_cast(u.l)), - checked::divide(static_cast(t.u), static_cast(u.u)) - ) + vmax( + checked::divide(static_cast(t.l), static_cast(u.l)), + checked::divide(static_cast(t.l), static_cast(u.u)), + checked::divide(static_cast(t.u), static_cast(u.l)), + checked::divide(static_cast(t.u), static_cast(u.u)) ) ) ; diff --git a/include/native.hpp b/include/native.hpp index 2e265c0..3da6ef1 100644 --- a/include/native.hpp +++ b/include/native.hpp @@ -15,6 +15,8 @@ #include #include +#include "checked.hpp" + // policy which creates results types and values equal to that of C++ promotions. // When used in conjunction with a desired exception policy, traps errors but // does not otherwise alter the results produced by the program using it. @@ -82,6 +84,25 @@ struct native { struct right_shift_result { typedef typename safe_type_promotion::type type; }; + + // forward to correct divide implementation + template + checked_result + static SAFE_NUMERIC_CONSTEXPR divide( + const T & t, + const U & u + ){ + return checked::divide(t, u); + } + // forward to correct modulus implementation + template + checked_result + static SAFE_NUMERIC_CONSTEXPR modulus( + const T & t, + const U & u + ){ + return checked::modulus(t, u); + } }; } // numeric diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index 67a46e8..649f846 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -26,7 +26,6 @@ #include "safe_base.hpp" #include "safe_compare.hpp" -#include "checked.hpp" #include "checked_result.hpp" #include "interval.hpp" @@ -146,6 +145,7 @@ operator R () const { checked_result r = checked::cast(m_t); if(r != exception_type::no_exception) E::range_error("conversion not possible"); + return static_cast(r); } @@ -305,9 +305,7 @@ SAFE_NUMERIC_CONSTEXPR inline operator+(const T & t, const U & u){ } // otherwise do the addition checking for overflow - checked_result r = checked::add( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()), + checked_result r = checked::add( base_value(t), base_value(u) ); @@ -375,9 +373,7 @@ SAFE_NUMERIC_CONSTEXPR operator-(const T & t, const U & u){ } // otherwise do the subtraction checking for overflow - checked_result r = checked::subtract( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()), + checked_result r = checked::subtract( base_value(t), base_value(u) ); @@ -451,9 +447,7 @@ SAFE_NUMERIC_CONSTEXPR operator*(const T & t, const U & u){ ); // otherwise do the multiplication checking for overflow - checked_result r = checked::multiply( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()), + checked_result r = checked::multiply( base_value(t), base_value(u) ); @@ -551,19 +545,18 @@ SAFE_NUMERIC_CONSTEXPR operator/(const T & t, const U & u){ ); // otherwise do the division checking for overflow - checked_result r = checked::divide2( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()), - base_value(t), - base_value(u) - ); + typedef typename dr::P::promotion_policy promotion_policy; + checked_result r = + promotion_policy::template divide( + base_value(t), + base_value(u) + ); r.template dispatch(); return result_type(static_cast(r)); } ///////////////////////////////////////////////////////////////// // modulus -/* template struct modulus_result { @@ -600,34 +593,30 @@ inline operator%(const T & t, const U & u){ typedef typename base_type::type u_base_type; // filter out case were overflow cannot occur - SAFE_NUMERIC_CONSTEXPR const interval t_interval = { - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; SAFE_NUMERIC_CONSTEXPR const interval u_interval = { base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - SAFE_NUMERIC_CONSTEXPR const interval r_interval - = operator%(t_interval, u_interval); - - // if no over/under flow possible - if(r_interval.no_exception()) + // if denominator includes zero + if(u_interval.l <= 0 && 0 <= u_interval.u){ + // no checking necessary return result_type(base_value(t) % base_value(u)); + } // otherwise do the modulus checking for overflow - checked_result r = checked::modulus( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()), - base_value(t), - base_value(u) - ); + typedef modulus_result mr; + typedef typename mr::P::promotion_policy promotion_policy; + checked_result r = + promotion_policy::template modulus( + base_value(t), + base_value(u) + ); r.template dispatch(); return result_type(static_cast(r)); } -*/ + ///////////////////////////////////////////////////////////////// // comparison diff --git a/test/test_checked_divide.cpp b/test/test_checked_divide.cpp index 9173e97..7024b86 100644 --- a/test/test_checked_divide.cpp +++ b/test/test_checked_divide.cpp @@ -30,8 +30,6 @@ bool test_checked_divide( typedef decltype(T1() / T2()) result_type; checked_result result = checked::divide( - std::numeric_limits::min(), - std::numeric_limits::max(), v1, v2 ); @@ -44,8 +42,6 @@ bool test_checked_divide( << " != "<< av1 << " / " << av2 << std::endl; result = checked::divide( - std::numeric_limits::min(), - std::numeric_limits::max(), v1, v2 ); @@ -58,8 +54,6 @@ bool test_checked_divide( << "erroneously detected error " << std::hex << result << av1 << " / " << av2 << std::dec << std::endl; result = checked::divide( - std::numeric_limits::min(), - std::numeric_limits::max(), v1, v2 ); @@ -142,6 +136,7 @@ int main(int argc, char *argv[]){ bool rval = true; TEST_EACH_VALUE_PAIR + std::cout << (rval ? "success!" : "failure") << std::endl; return ! rval ; } diff --git a/test/test_divide.cpp b/test/test_divide.cpp index 4e58c0a..3c4fca2 100644 --- a/test/test_divide.cpp +++ b/test/test_divide.cpp @@ -87,5 +87,6 @@ const char *test_division_result[VALUE_ARRAY_SIZE] = { int main(int argc, char * argv[]){ bool rval = true; TEST_EACH_VALUE_PAIR + std::cout << (rval ? "success!" : "failure") << std::endl; return ! rval ; } diff --git a/test/test_divide_auto.cpp b/test/test_divide_auto.cpp index 8980d05..5ad7566 100644 --- a/test/test_divide_auto.cpp +++ b/test/test_divide_auto.cpp @@ -91,5 +91,6 @@ const char *test_division_result[VALUE_ARRAY_SIZE] = { int main(int argc, char * argv[]){ bool rval = true; TEST_EACH_VALUE_PAIR + std::cout << (rval ? "success!" : "failure") << std::endl; return ! rval ; }