From 9ba1617c4e109f4bf25bc3fb5eaed87b9c73aab1 Mon Sep 17 00:00:00 2001 From: Robert Ramey Date: Mon, 15 Jun 2015 16:18:30 -0700 Subject: [PATCH] Implemented divide operations updated test_add --- include/checked.hpp | 205 +++++++++++---------------- include/checked_result.hpp | 11 +- include/concept/exception_policy.hpp | 1 + include/exception_policies.hpp | 14 +- include/interval.hpp | 100 ++++++++++++- include/native.hpp | 28 ++-- include/safe_base_operations.hpp | 76 ++++++++-- test/test_add.cpp | 11 +- test/test_divide.cpp | 170 +++++++++++----------- 9 files changed, 367 insertions(+), 249 deletions(-) diff --git a/include/checked.hpp b/include/checked.hpp index 9754108..9bb9ca9 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -381,7 +381,7 @@ namespace checked { const T & t, const U & u ) { - static_assert(! is_safe::value, "should only be a base type here!"); + static_assert(! is_safe::value, "should not be a base type here!"); return detail::cast(t) != checked_result::exception_type::no_exception ? detail::cast(t) @@ -410,134 +410,88 @@ namespace checked { ; } -/* + //////////////////////////////// + // safe division on unsafe types namespace detail { - //////////////////////////////// - // multiplication implementation - - template - decltype(T() * U()) - check_multiplication_overflow(const T & t, const U & u){ - typedef decltype(T() * U()) result_type; - char const * const msg = "safe range multiplication overflow"; - // presume that size of uintmax_t and intmax_t are the same - typedef bits available_bits; - - if(multiply_result_bits::value - <= boost::numeric::bits::value) - return t * u; - - if(multiply_result_bits::value <= available_bits::value){ - typedef typename multiply_result_type::type temp_type; - temp_type tmp = static_cast(t) * temp_type(u); - // the following works for both positive and negative results - // and for both signed and unsigned numbers - if(tmp > boost::integer_traits::const_max) - boost::numeric::overflow(msg); - if(tmp < boost::integer_traits::const_min) - boost::numeric::overflow(msg); - return static_cast(tmp); - } - - // when the there is no native type which can hold the product - // use multible precision - - // t is factored as (a << temp_bits) + b - // u is factored as (c << temp_bits) + d - // so we use multi-precision: - // a + b - // c + d - // ----- - // bd - // ad - // cb - // ac - // ----- - // .. - - if(boost::numeric::is_unsigned::value - && (t < 0 || u < 0)) - overflow("conversion of negative value to unsigned"); - - if(t == 1) - return u; - if(u == 1) - return t; - - result_type rt = t; - if(rt < 0){ - rt = ~rt + 1; - // address - if(rt < 0) - overflow("overflow of negative value"); - } - result_type ru = u; - if(ru < 0){ - ru = ~ru + 1; - // address - if(ru < 0) - overflow("overflow of negative value"); - } - - // check positive values for overflow - - // t is factored as (a << temp_bits) + b - // u is factored as (c << temp_bits) + d - // so we use multi-precision: - // a + b - // c + d - // ----- - // bd - // ad - // cb - // ac - // ----- - // .. - - typedef boost::uintmax_t accumulator_type; - const int temp_bits = bits::value / 2; - typedef typename boost::uint_t::least temp_type; - - temp_type a = (static_cast(rt) >> temp_bits); - temp_type c = (static_cast(ru) >> temp_bits); - if(0 != a && 0 != c) - overflow(msg); - - temp_type b = static_cast(rt); - if((static_cast(b) * static_cast(c) >> temp_bits) > 0) - overflow(msg); - - temp_type d = static_cast(ru); - if(0 != (static_cast(a) * static_cast(d) >> temp_bits)) - overflow(msg); - - return t * u; + template + typename boost::enable_if_c< + std::is_unsigned::value, + checked_result + >::type + SAFE_NUMERIC_CONSTEXPR divide( + const R & minr, + const R & maxr, + const R t, + const R u + ) { + return checked_result(t / u); } - template - decltype(T() / U()) - check_division_overflow(const T & t, const U & u){ - if(0 == u) - overflow("divide by zero"); - if(boost::numeric::is_signed::value){ - // t unsigned, u signed - if(boost::numeric::is_unsigned::value){ - if(u < 0){ - overflow("conversion of negative value to unsigned"); - } - } - else{ - // both signed - // pathological case: change sign on negative number so it overflows - if(t == boost::integer_traits::const_min && u == -1) - overflow("overflow in result"); - } - } - // both unsigned - // t signed, u unsigned - return t / u; + template + typename boost::enable_if_c< + std::is_signed::value, + checked_result + >::type + SAFE_NUMERIC_CONSTEXPR divide( + const R & minr, + const R & maxr, + const R t, + const R u + ){ + return + // note presumption of two's complement arithmetic + (u < 0 && t == std::numeric_limits::min()) ? + checked_result( + checked_result::exception_type::domain_error, + "divide by zero" + ) + : + 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(! is_safe::value, "should not be a base type here!"); + return + detail::cast(t) != checked_result::exception_type::no_exception ? + detail::cast(t) + : + detail::cast(u) != checked_result::exception_type::no_exception ? + detail::cast(u) + : + u == 0 ? + checked_result( + 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 + ) + ; + } + /* template decltype(T() / U()) check_modulus_overflow(const T & t, const U & u){ @@ -562,8 +516,7 @@ namespace checked { // t signed, u unsigned return t % u; } -} // detail -*/ + */ } // checked } // numeric } // boost diff --git a/include/checked_result.hpp b/include/checked_result.hpp index eb47a48..c1bb21b 100644 --- a/include/checked_result.hpp +++ b/include/checked_result.hpp @@ -23,7 +23,8 @@ struct checked_result { no_exception, overflow_error, underflow_error, - range_error + range_error, + domain_error }; exception_type m_e; union { @@ -103,6 +104,9 @@ struct checked_result { case checked_result::exception_type::range_error: EP::range_error(m_msg); break; + case checked_result::exception_type::domain_error: + EP::domain_error(m_msg); + break; case checked_result::exception_type::no_exception: break; default: @@ -112,7 +116,7 @@ struct checked_result { }; template -SAFE_NUMERIC_CONSTEXPR inline const checked_result minxx(const checked_result & t, const checked_result & u){ +SAFE_NUMERIC_CONSTEXPR inline const checked_result min(const checked_result & t, const checked_result & u){ return (t.m_e == checked_result::exception_type::no_exception && u.m_e == checked_result::exception_type::no_exception) ? @@ -127,8 +131,9 @@ SAFE_NUMERIC_CONSTEXPR inline const checked_result minxx(const checked_result ) ; } + template -SAFE_NUMERIC_CONSTEXPR inline const checked_result maxxx(const checked_result & t, const checked_result & u){ +SAFE_NUMERIC_CONSTEXPR inline const checked_result max(const checked_result & t, const checked_result & u){ return (t.m_e == checked_result::exception_type::no_exception && u.m_e == checked_result::exception_type::no_exception) ? diff --git a/include/concept/exception_policy.hpp b/include/concept/exception_policy.hpp index 4a1611e..da7b680 100644 --- a/include/concept/exception_policy.hpp +++ b/include/concept/exception_policy.hpp @@ -24,6 +24,7 @@ struct ExceptionPolicy { EP::overflow_error(message); EP::underflow_error(message); EP::range_error(message); + EP::domain_error(message); } }; diff --git a/include/exception_policies.hpp b/include/exception_policies.hpp index 99366d1..bdae4d9 100644 --- a/include/exception_policies.hpp +++ b/include/exception_policies.hpp @@ -31,6 +31,7 @@ struct ignore_exception { static void overflow_error(const char * message) {} static void underflow_error(const char * message) {} static void range_error(const char * message) {} + static void domain_error(const char * message) {} }; // If an exceptional condition is detected at runtime throw the exception. @@ -45,6 +46,9 @@ struct throw_exception { static void range_error(const char * message) { throw std::domain_error(message); } + static void domain_error(const char * message) { + throw std::domain_error(message); + } }; // example - if you want to specify specific behavior for particular exception @@ -54,7 +58,8 @@ struct throw_exception { template< void (*OVERFLOW)(const char *), void (*UNDERFLOW)(const char *), - void (*RANGE)(const char *) + void (*RANGE)(const char *), + void (*DOMAIN)(const char *) > struct no_exception_support { BOOST_CONCEPT_ASSERT((boost::numeric::ExceptionPolicy)); @@ -67,6 +72,9 @@ struct no_exception_support { static void range_error(const char * message) { RANGE(message); } + static void domain_error(const char * message) { + DOMAIN(message); + } }; // use this policy to trap at compile time any operation which @@ -90,6 +98,10 @@ struct trap_exception { static void range_error(const T *) { static_assert(std::is_void::value, "range_error"); } + template + static void domain_error(const T *) { + static_assert(std::is_void::value, "domain_error"); + } }; } // namespace numeric diff --git a/include/interval.hpp b/include/interval.hpp index 179b431..f27894e 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -54,11 +54,11 @@ SAFE_NUMERIC_CONSTEXPR interval operator*(const interval & t, const interv (u.l < 0) ? (u.u > 0) ? // M * M interval( - boost::numeric::minxx( + min( checked::multiply(t.l, u.u), checked::multiply(t.u, u.l) ), - boost::numeric::maxxx( + max( checked::multiply(t.l, u.u), checked::multiply(t.u, u.l) ) @@ -119,6 +119,102 @@ SAFE_NUMERIC_CONSTEXPR interval operator*(const interval & t, const interv ; } +template +SAFE_NUMERIC_CONSTEXPR interval operator/(const interval & t, const interval & u){ + // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic + return + (u.l <= 0) ? + interval( + 0, + checked_result( + checked_result::exception_type::domain_error, + "interval divisor includes zero" + ) + ) + : + interval( + min( + min( + checked::divide(t.l, u.l), + checked::divide(t.l, u.u) + ), + min( + checked::divide(t.u, u.l), + checked::divide(t.u, u.u) + ) + ), + max( + max( + checked::divide(t.l, u.l), + checked::divide(t.l, u.u) + ), + max( + checked::divide(t.u, u.l), + checked::divide(t.u, u.u) + ) + ) + ) + ; +} + +/* +template +SAFE_NUMERIC_CONSTEXPR interval operator/(const interval & t, const interval & u){ + // adapted from boost interval library + return + (t.u < 0) ? + (u.u < 0) ? + interval( + checked_result(t.u, u.l), + checked_result(t.l, u.u) + ) + : + interval( + checked_result(t.l, u.l), + checked_result(t.u, u.u) + ) + : + (t.l < 0) ? + (u.u < 0) ? + interval( + checked_result(t.u, u.u), + checked_result(t.l, u.u) + ) + : + interval( + checked_result(t.l, u.l), + checked_result(t.u, u.l) + ) + : + (u.u < 0) ? + interval( + checked_result(t.u, u.u), + checked_result(t.l, u.l) + ) + : + interval( + checked_result(t.l, u.u), + checked_result(t.u, u.l) + ) + ; +} + if (::boost::numeric::interval_lib::user::is_neg(xu)) + if (::boost::numeric::interval_lib::user::is_neg(yu)) + return I(rnd.div_down(xu, yl), rnd.div_up(xl, yu), true); + else + return I(rnd.div_down(xl, yl), rnd.div_up(xu, yu), true); + else if (::boost::numeric::interval_lib::user::is_neg(xl)) + if (::boost::numeric::interval_lib::user::is_neg(yu)) + return I(rnd.div_down(xu, yu), rnd.div_up(xl, yu), true); + else + return I(rnd.div_down(xl, yl), rnd.div_up(xu, yl), true); + else + if (::boost::numeric::interval_lib::user::is_neg(yu)) + return I(rnd.div_down(xu, yu), rnd.div_up(xl, yl), true); + else + return I(rnd.div_down(xl, yu), rnd.div_up(xu, yl), true); +*/ + } // numeric } // boost diff --git a/include/native.hpp b/include/native.hpp index 0ecb2e2..14db46d 100644 --- a/include/native.hpp +++ b/include/native.hpp @@ -30,42 +30,34 @@ template< struct safe; struct native { - template< - typename T, - typename U, - typename P, - typename E - > + template struct addition_result { typedef typename base_type::type base_type_t; typedef typename base_type::type base_type_u; typedef decltype(base_type_t() + base_type_u()) result_base_type; typedef safe type; }; - template< - typename T, - typename U, - typename P, - typename E - > + template struct subtraction_result { typedef typename base_type::type base_type_t; typedef typename base_type::type base_type_u; typedef decltype(base_type_t() - base_type_u()) result_base_type; typedef safe type; }; - template< - typename T, - typename U, - typename P, - typename E - > + template struct multiplication_result { typedef typename base_type::type base_type_t; typedef typename base_type::type base_type_u; typedef decltype(base_type_t() * base_type_u()) result_base_type; typedef safe type; }; + template + struct division_result { + typedef typename base_type::type base_type_t; + typedef typename base_type::type base_type_u; + typedef decltype(base_type_t() / base_type_u()) result_base_type; + typedef safe type; + }; }; } // numeric diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index d52ab42..30afb0f 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -310,22 +310,74 @@ inline operator*(const T & t, const U & u){ return static_cast(r); } -/* ///////////////////////////////////////////////////////////////// // division -// special case - possible overflow -template -typename boost::enable_if< - std::is_integral, - decltype(T() / Stored()) + +template +struct division_result { + typedef common_policies P; + typedef typename P::promotion_policy::template division_result< + T, + U, + typename P::promotion_policy, + typename P::exception_policy + >::type type; +}; + +template +typename boost::lazy_enable_if< + boost::mpl::or_< + boost::numeric::is_safe, + boost::numeric::is_safe + >, + division_result >::type -inline operator/(const T & lhs, const safe_base & rhs) { - if(safe_compare::equal(0, rhs)) - throw std::domain_error("Divide by zero"); - return static_cast< - decltype(T() / Stored()) - >(lhs / static_cast(rhs)); +inline operator/(const T & t, const U & u){ + // argument dependent lookup should guarentee that we only get here + // only if one of the types is a safe type. Verify this here + typedef division_result ar; + typedef typename ar::type result_type; + static_assert( + boost::numeric::is_safe::value, + "Promotion failed to return safe type" + ); + + typedef typename base_type::type result_base_type; + typedef typename base_type::type t_base_type; + 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()) + return result_type(base_value(t) / base_value(u)); + + // otherwise do the multiplication checking for overflow + checked_result r = checked::divide( + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()), + base_value(t), + base_value(u) + ); + + r.template dispatch(); + + return static_cast(r); } + +/* + // comparison operators template typename boost::enable_if< diff --git a/test/test_add.cpp b/test/test_add.cpp index 747d919..6d9cc7e 100644 --- a/test/test_add.cpp +++ b/test/test_add.cpp @@ -27,10 +27,12 @@ bool test_add( try{ result = t1 + v2; + static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" ); + if(expected_result == 'x'){ std::cout << "failed to detect error in addition " @@ -66,14 +68,6 @@ bool test_add( // presuming native policy boost::numeric::safe result; - static_assert( - std::is_same< - boost::numeric::safe, - decltype(t1 + t2) - >::value, - "unexpected result type" - ); - try{ result = t1 + t2; @@ -81,6 +75,7 @@ bool test_add( boost::numeric::is_safe::value, "Expression failed to return safe type" ); + if(expected_result == 'x'){ std::cout << "failed to detect error in addition " diff --git a/test/test_divide.cpp b/test/test_divide.cpp index 573b053..5837984 100644 --- a/test/test_divide.cpp +++ b/test/test_divide.cpp @@ -27,74 +27,86 @@ bool test_divide( << "testing " << av1 << " / " << av2 << std::endl; + { + boost::numeric::safe t1 = v1; + // presuming native policy + boost::numeric::safe result; - boost::numeric::safe t1 = v1; - BOOST_TYPEOF_TPL(T1() / T2()) result; - - try{ - result = t1 / v2; - - if(expected_result != '.'){ - //if(expected_result == 'x'){ - std::cout - << "failed to detect error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " ! = "<< av1 << " / " << av2 - << std::endl; - try{ - result = t1 / v2; + try{ + result = t1 / v2; + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); + if(expected_result == 'x'){ + std::cout + << "failed to detect error in division " + << std::hex << result << "(" << std::dec << result << ")" + << " ! = "<< av1 << " / " << av2 + << std::endl; + try{ + t1 / v2; + } + catch(...){} + return false; + } + } + catch(std::exception & e){ + if(expected_result == '.'){ + std::cout + << "erroneously detected error in division " + << std::hex << result << "(" << std::dec << result << ")" + << " == "<< av1 << " / " << av2 + << std::endl; + try{ + t1 / v2; + } + catch(...){} + return false; } - catch(...){} - return false; } } - catch(std::range_error){ - if(expected_result != 'x'){ - //if(expected_result == '.'){ - std::cout - << "erroneously detected error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " == "<< av1 << " / " << av2 - << std::endl; - try{ - result = t1 / v2; - } - catch(...){} - return false; - } - } - boost::numeric::safe t2 = v2; + { + boost::numeric::safe t1 = v1; + boost::numeric::safe t2 = v2; - try{ - result = t1 / t2; + // presuming native policy + boost::numeric::safe result; - if(expected_result != '.'){ - //if(expected_result == 'x'){ - std::cout - << "failed to detect error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " ! = "<< av1 << " / " << av2 - << std::endl; - try{ - result = t1 / t2; + try{ + result = t1 / t2; + + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); + + if(expected_result == 'x'){ + std::cout + << "failed to detect error in division " + << std::hex << result << "(" << std::dec << result << ")" + << " ! = "<< av1 << " / " << av2 + << std::endl; + try{ + t1 / t2; + } + catch(...){} + return false; } - catch(...){} - return false; } - } - catch(std::range_error){ - if(expected_result != 'x'){ - //if(expected_result == '.'){ - std::cout - << "erroneously detected error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " == "<< av1 << " / " << av2 - << std::endl; - try{ - result = t1 / t2; + catch(std::exception & e){ + if(expected_result == '.'){ + std::cout + << "erroneously detected error in division " + << std::hex << result << "(" << std::dec << result << ")" + << " == "<< av1 << " / " << av2 + << std::endl; + try{ + t1 / t2; + } + catch(...){} + return false; } - catch(...){} - return false; } } return true; @@ -113,38 +125,38 @@ const char *test_division_result[VALUE_ARRAY_SIZE] = { // 01234567890123456789012345678901 /* 0*/ "................................", /* 1*/ "................................", -/* 2*/ "...x...x...x...x................", -/* 3*/ "................................", +/* 2*/ "........................xxxxxxxx", +/* 3*/ "........................xxxxxxxx", /* 4*/ ".................................", /* 5*/ "................................", -/* 6*/ "...x...x...x...x................", -/* 7*/ "................................", +/* 6*/ "........................xxxxxxxx", +/* 7*/ "........................xxxxxxxx", /* 8*/ "................................", /* 9*/ "................................", -/*10*/ "...x...x...x...x................", -/*11*/ "................................", +/*10*/ "..xx..xx..xx............xxxxxxxx", +/*11*/ "........................xxxxxxxx", /*12*/ "................................", /*13*/ "................................", -/*14*/ "...x...x...x...x................", -/*15*/ "................................", +/*14*/ "..xx..xx..xx..xx............xxxx", +/*15*/ "............................xxxx", // 0 0 0 0 // 01234567012345670123456701234567 // 01234567890123456789012345678901 -/*16*/ "..xx..xx..xx..xx................", -/*17*/ "..xx..xx..xx..xx................", -/*18*/ "..xx..xx..xx..xx................", -/*19*/ "..xx..xx..xx..xx................", -/*20*/ "..xx..xx..xx..xx................", -/*21*/ "..xx..xx..xx..xx................", -/*22*/ "..xx..xx..xx..xx................", -/*23*/ "..xx..xx..xx..xx................", +/*16*/ "................................", +/*17*/ "................................", +/*18*/ "................................", +/*19*/ "................................", +/*20*/ "................................", +/*21*/ "................................", +/*22*/ "................................", +/*23*/ "................................", -/*24*/ "..xx..xx..xx..xx................", -/*25*/ "..xx..xx..xx..xx................", -/*26*/ "..xx..xx..xx..xx................", -/*27*/ "..xx..xx..xx..xx................", +/*24*/ "..xx..xx..xx....................", +/*25*/ "..xx..xx..xx....................", +/*26*/ "..xx..xx..xx....................", +/*27*/ "..xx..xx..xx....................", /*28*/ "..xx..xx..xx..xx................", /*29*/ "..xx..xx..xx..xx................", /*30*/ "..xx..xx..xx..xx................",