diff --git a/include/automatic.hpp b/include/automatic.hpp index 493ad37..38fe4b8 100644 --- a/include/automatic.hpp +++ b/include/automatic.hpp @@ -19,12 +19,11 @@ #include #include // (u)intmax_t, -#include // true_type, false_type +#include // true_type, false_type, is_same #include -#include -#include #include "safe_common.hpp" +#include "checked_result.hpp" #include "interval.hpp" #include "safe_range.hpp" @@ -147,13 +146,13 @@ struct automatic { SAFE_NUMERIC_CONSTEXPR static const interval< max_t> r = operator+(t, u); - SAFE_NUMERIC_CONSTEXPR static const max_t newmin = r.l.is_valid() ? + SAFE_NUMERIC_CONSTEXPR static const max_t newmin = r.l.no_exception() ? static_cast(r.l) : std::numeric_limits::min() ; - SAFE_NUMERIC_CONSTEXPR static const max_t newmax = r.u.is_valid() ? + SAFE_NUMERIC_CONSTEXPR static const max_t newmax = r.u.no_exception() ? static_cast(r.u) : std::numeric_limits::max() @@ -209,25 +208,24 @@ struct automatic { }; typedef calculate_max_t max_t; + // typedef typename print::type p_max_t; SAFE_NUMERIC_CONSTEXPR static const interval< max_t> r = operator-(t, u); - SAFE_NUMERIC_CONSTEXPR static const max_t newmin = r.l.is_valid() ? + SAFE_NUMERIC_CONSTEXPR static const max_t newmin = r.l.no_exception() ? static_cast(r.l) : std::numeric_limits::min() ; + // typedef print> p_newmin; - SAFE_NUMERIC_CONSTEXPR static const max_t newmax = r.u.is_valid() ? + SAFE_NUMERIC_CONSTEXPR static const max_t newmax = r.u.no_exception() ? static_cast(r.u) : std::numeric_limits::max() ; - - // typedef typename print::type p_max_t; - // typedef typename print>::type p_newmin; - // typedef typename print>::type p_newmax; + // typedef print> p_newmax; static_assert( newmin < newmax, @@ -268,13 +266,13 @@ struct automatic { SAFE_NUMERIC_CONSTEXPR static const interval< max_t> r = operator*(t, u); - SAFE_NUMERIC_CONSTEXPR static const max_t newmin = r.l.is_valid() ? + SAFE_NUMERIC_CONSTEXPR static const max_t newmin = r.l.no_exception() ? static_cast(r.l) : std::numeric_limits::min() ; - SAFE_NUMERIC_CONSTEXPR static const max_t newmax = r.u.is_valid() ? + SAFE_NUMERIC_CONSTEXPR static const max_t newmax = r.u.no_exception() ? static_cast(r.u) : std::numeric_limits::max() @@ -304,23 +302,197 @@ struct automatic { }; template struct division_result { - typedef typename base_type::type t_base_type; - typedef typename base_type::type u_base_type; - SAFE_NUMERIC_CONSTEXPR static const interval t = { + 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" + ); + + SAFE_NUMERIC_CONSTEXPR static interval t(){ + return interval( + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + ); + }; + SAFE_NUMERIC_CONSTEXPR static interval u(){ + return interval( + base_value(std::numeric_limits::min()), + 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 = { + SAFE_NUMERIC_CONSTEXPR static const interval u = { 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 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, + std::intmax_t, + std::uintmax_t + >::type; - typedef safe_base type; + template + SAFE_NUMERIC_CONSTEXPR static const interval r_upper(const interval & t){ + static_assert( + std::is_literal_type< interval >::value, + "interval is not literal type" + ); + return interval( + max(checked_result(1), t.l), + t.u + ); + } + template + SAFE_NUMERIC_CONSTEXPR static const interval r_lower(const interval & t){ + static_assert( + std::is_literal_type< interval >::value, + "interval is not literal type" + ); + return interval( + t.l, + min(checked_result(-1), t.u) + ); + } + + // result unsigned + template + static typename boost::enable_if_c< + ! std::numeric_limits::is_signed, + const interval + >::type + SAFE_NUMERIC_CONSTEXPR r( + const interval & t, + const interval & u + ){ + if(u.l > 0) + return operator/(t, u); + else + return operator/(t, r_upper(u)) ; + }; + + // result signed + template + static typename boost::enable_if_c< + std::numeric_limits::is_signed, + const interval + >::type + SAFE_NUMERIC_CONSTEXPR r( + const interval & t, + const interval & u + ){ + if(u.l > 0 || u.u < 0){ + auto retval = operator/(t, u); + return retval; + } + else{ + auto lower = operator/(t,r_lower(u)); + auto upper = operator/(t,r_upper(u)); + return + interval( + min(lower.l, upper.l), + max(lower.u, upper.u) + ); + } + } + + /* + template + SAFE_NUMERIC_CONSTEXPR static const interval r( + const interval & t, + const interval & u + ){ + typedef print p_Tx_t; + typedef print p_Ux_t; + if(std::numeric_limits::is_signed){ + typedef print p_max_t; + static_assert( + std::numeric_limits::is_signed, + "signed type expected" + ); + if(u.l > 0 || u.u < 0){ + auto retval = operator/(t, u); + // typedef print p_retval; + return retval; + //return operator/(t, u); + } + else{ + auto lower = operator/(t,r_lower(u)); + // typedef print p_lower; + auto upper = operator/(t,r_upper(u)); + // typedef print p_lower; + return + interval< max_t>( + min(lower.l, upper.l), + max(lower.u, upper.u) + ); + } + } + else{ // u is unsigned + if(u.l > 0) + return operator/(t, u); + else + return operator/(t, r_upper(u)) ; + }; + }; + */ + + //typedef print 0) >> p_ulgt0; + //typedef print> p_uult0; + + constexpr const static interval ni = r(t(), u()); + + //static_assert(ni.l.no_exception(), "invalid lower bound"); + //static_assert(ni.u.no_exception(), "invalid upper bound"); + + SAFE_NUMERIC_CONSTEXPR static const max_t newmin = ni.l.no_exception() ? + static_cast(ni.l) + : + std::numeric_limits::min() + ; + //typedef print> p_newmin; + + SAFE_NUMERIC_CONSTEXPR static const max_t newmax = ni.u.no_exception() ? + static_cast(ni.u) + : + std::numeric_limits::max() + ; + //typedef print> p_newmax; + + static_assert( + newmin <= newmax, + "new minimumum must be less than new maximum" + ); + + static_assert( + std::numeric_limits::min() >= 0 || std::is_signed::value, + "newmin < 0 and unsigned can't happen" + ); + + typedef typename safe_range< + max_t, + newmin, + newmax, + P, + E + >::type type; }; + template struct modulus_result { typedef typename base_type::type t_base_type; diff --git a/include/checked.hpp b/include/checked.hpp index 5f4b38d..5f10fc9 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -16,9 +16,11 @@ // C++ types. #include -#include // is_integral +#include // is_integral, make_unsigned #include +#include +#include #include "safe_common.hpp" #include "checked_result.hpp" @@ -455,42 +457,514 @@ SAFE_NUMERIC_CONSTEXPR checked_result multiply( //////////////////////////////// // safe division on unsafe types + namespace detail { -template +template typename boost::enable_if_c< - std::is_unsigned::value, + ! std::is_signed::value && ! std::is_signed::value, checked_result >::type SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const R t, - const R u -) { + const T & t, + const U & u +){ return checked_result(t / u); } -template +template typename boost::enable_if_c< - std::is_signed::value, + std::is_signed::value && std::is_signed::value, checked_result >::type SAFE_NUMERIC_CONSTEXPR divide( - const R & minr, - const R & maxr, - const R t, - const R u + const T & t, + const U & u ){ return - // note presumption of two's complement arithmetic - (u < 0 && t == std::numeric_limits::min()) ? + (u == -1 && t == std::numeric_limits::min()) ? + checked_result( + exception_type::domain_error, + "result cannot be represented" + ) + : + checked_result(static_cast(t) / u) + ; +} + +template +typename boost::enable_if_c< + ! std::is_signed::value && std::is_signed::value, + checked_result +>::type +SAFE_NUMERIC_CONSTEXPR divide( + const T & t, + const U & u +){ + return + (u >= 0) ? + checked_result(t / static_cast>(u)) + : + (u == -1) ? + (u == std::numeric_limits::min()) ? + checked_result( + exception_type::domain_error, + "result cannot be represented" + ) + : + checked_result(- (t / -u)) + : + // u < -1 + 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 T & t, + const U & u +){ + return + checked_result(t / u); + ; +} + +} // detail + +template +checked_result +SAFE_NUMERIC_CONSTEXPR divide( + const T & t, + const U & u +){ + const checked_result rt = cast(t); + const checked_result ru = cast(u); + return + (u == 0) ? checked_result( exception_type::domain_error, "divide by zero" ) : + detail::divide( + static_cast(rt), + static_cast(ru) + ; +} + + +#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 +checked_result +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) + ); +} + +template +checked_result +SAFE_NUMERIC_CONSTEXPR divide( + const R & minr, + const R & maxr, + 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) + ; +} + +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); ; } @@ -505,22 +979,27 @@ SAFE_NUMERIC_CONSTEXPR checked_result divide( ) { 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) + // cast(u) != exception_type::no_exception ? + // cast(u) + // : + (u == 0) ? + checked_result( + exception_type::domain_error, + "divide by zero" + ) : - u == 0 ? - checked_result( - exception_type::domain_error, - "divide by zero" - ) - : - detail::divide(minr, maxr, t, u) + detail::divide(minr, maxr, t, u) + ; ; } + +/* template SAFE_NUMERIC_CONSTEXPR checked_result divide( const T & t, @@ -535,9 +1014,13 @@ SAFE_NUMERIC_CONSTEXPR checked_result divide( ) ; } +*/ +#endif //////////////////////////////// // safe modulus on unsafe types + +/* namespace detail { template @@ -618,7 +1101,7 @@ SAFE_NUMERIC_CONSTEXPR checked_result modulus( ) ; } - +*/ /////////////////////////////////// // shift operations @@ -641,7 +1124,7 @@ SAFE_NUMERIC_CONSTEXPR checked_result check_shift( const checked_result ru = cast(u); // INT34-C C++ standard paragraph 5.8 return - ( ! ru.is_valid()) ? + ( ! ru.no_exception()) ? ru : ( ru < 0) ? @@ -670,7 +1153,7 @@ SAFE_NUMERIC_CONSTEXPR checked_result left_shift( ) { const checked_result r = detail::check_shift(t, u); return - (! r.is_valid()) ? + (! r.no_exception()) ? r : checked_result(static_cast(r) << t); @@ -685,7 +1168,7 @@ SAFE_NUMERIC_CONSTEXPR checked_result right_shift( ) { const checked_result r = detail::check_shift(t, u); return - (! r.is_valid()) ? + (! r.no_exception()) ? r : checked_result(static_cast(r) >> t); diff --git a/include/checked_result.hpp b/include/checked_result.hpp index a818ebc..f6d061f 100644 --- a/include/checked_result.hpp +++ b/include/checked_result.hpp @@ -27,14 +27,15 @@ enum class exception_type { underflow_error, range_error, domain_error, - uninitialized + uninitialized, + static_assertion }; template struct checked_result { // poor man's variant which supports SAFE_NUMERIC_CONSTEXPR - const exception_type m_e; - const union { + exception_type m_e; + union { R m_r; char const * m_msg; }; @@ -56,30 +57,40 @@ struct checked_result { m_e(exception_type::uninitialized), m_r(0) {} + template + SAFE_NUMERIC_CONSTEXPR //explicit// checked_result(const checked_result & t) : + m_e(t.m_e), + m_r(t.m_r) + {} */ + SAFE_NUMERIC_CONSTEXPR /*explicit*/ checked_result(const R & r) : m_e(exception_type::no_exception), m_r(r) {} + SAFE_NUMERIC_CONSTEXPR /*explicit*/ checked_result( exception_type e, - const char * msg = nullptr + const char * msg ) : m_e(e), m_msg(msg) {} + // accesors SAFE_NUMERIC_CONSTEXPR operator R() const { // assert(exception_type::no_exception == m_e); return m_r; } + /* SAFE_NUMERIC_CONSTEXPR operator exception_type() const { return m_e; } SAFE_NUMERIC_CONSTEXPR operator const char *() const { - assert(exception_type::no_exception != m_e); + // assert(exception_type::no_exception != m_e); return m_msg; } + */ template SAFE_NUMERIC_CONSTEXPR boost::logic::tribool operator<(const checked_result & t) const { @@ -120,7 +131,7 @@ struct checked_result { SAFE_NUMERIC_CONSTEXPR bool operator!=(const exception_type & et) const { return m_e != et; } - SAFE_NUMERIC_CONSTEXPR bool is_valid() const { + SAFE_NUMERIC_CONSTEXPR bool no_exception() const { return m_e == exception_type::no_exception; } @@ -195,7 +206,10 @@ SAFE_NUMERIC_CONSTEXPR inline const checked_result min( } template -SAFE_NUMERIC_CONSTEXPR inline const checked_result max(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 == exception_type::no_exception && u.m_e == exception_type::no_exception) ? @@ -225,25 +239,52 @@ std::ostream & operator<<(std::ostream & os, const boost::numeric::checked_resul if(r == boost::numeric::exception_type::no_exception) os << static_cast(r); else - os << static_cast(r); + os << r.m_msg; //static_cast(r); return os; } template<> std::ostream & operator<<(std::ostream & os, const boost::numeric::checked_result & r){ if(r == boost::numeric::exception_type::no_exception) - os << std::dec << static_cast(r); + os << static_cast(r); else - os << static_cast(r); + os << r.m_msg; //static_cast(r); return os; } +template<> +std::ostream & operator<<(std::ostream & os, const boost::numeric::checked_result & r){ + if(r == boost::numeric::exception_type::no_exception) + os << static_cast(r); + else + os << r.m_msg; //static_cast(r); + return os; +} + +/* template std::istream & operator>>(std::istream & is, const boost::numeric::checked_result & r){ is >> r.m_r; return is; } +template +std::istream & operator>>(std::istream & is, const boost::numeric::checked_result & r){ + std::int16_t i; + is >> i; + r.m_r = i; + return is; +} + +template +std::istream & operator>>(std::istream & is, const boost::numeric::checked_result & r){ + std::uint16_t i; + is >> i; + r.m_r = i; + return is; +} +*/ + } // std ///////////////////////////////////////////////////////////////// diff --git a/include/exception_policies.hpp b/include/exception_policies.hpp index d163f7f..253ad7c 100644 --- a/include/exception_policies.hpp +++ b/include/exception_policies.hpp @@ -25,10 +25,10 @@ namespace numeric { // this would emulate the normal C/C++ behavior of permitting overflows // and the like. struct ignore_exception { - constexpr static void overflow_error(const char * message) {} - constexpr static void underflow_error(const char * message) {} - constexpr static void range_error(const char * message) {} - constexpr static void domain_error(const char * message) {} + 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) {} }; // example - if you want to specify specific behavior for particular exception diff --git a/include/interval.hpp b/include/interval.hpp index 23a87b9..6f79c80 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -14,9 +14,11 @@ #include #include +#include #include + #include "checked_result.hpp" #include "checked.hpp" @@ -25,33 +27,71 @@ namespace numeric { template struct interval { + static_assert( + std::is_literal_type< checked_result >::value, + "is literal type" + ); + const checked_result l; const checked_result u; /* + SAFE_NUMERIC_CONSTEXPR checked_result get_l() const { + return l; + } + SAFE_NUMERIC_CONSTEXPR checked_result get_u() const { + return u; + } + template + SAFE_NUMERIC_CONSTEXPR interval(const T & lower, const U & upper) : + l(checked::cast(lower)), + u(checked::cast(upper)) + {} + SAFE_NUMERIC_CONSTEXPR interval( + const R & lower, + const R & upper + ) : + l(checked_result(lower)), + u(checked_result(upper)) + {} + SAFE_NUMERIC_CONSTEXPR interval(const interval & rhs) : + l(rhs.l), + u(rhs.u) + {} + template + SAFE_NUMERIC_CONSTEXPR interval( + const T & lower, + const T & upper + ) : + l(checked_result(lower)), + u(checked_result(upper)) + {} template SAFE_NUMERIC_CONSTEXPR interval(const T & lower, const U & upper) : l(checked::cast(lower)), u(checked::cast(upper)) {} */ - SAFE_NUMERIC_CONSTEXPR interval( - const checked_result & lower, - const checked_result & upper - ) : - l(checked_result(lower)), - u(checked_result(upper)) + template + SAFE_NUMERIC_CONSTEXPR interval(const T & lower, const T & upper) : + l(checked::cast(lower)), + u(checked::cast(upper)) {} - SAFE_NUMERIC_CONSTEXPR interval(const R & lower, const R & upper) : - l(checked_result(lower)), - u(checked_result(upper)) - {} - /* - SAFE_NUMERIC_CONSTEXPR interval(const interval & rhs) : + template + SAFE_NUMERIC_CONSTEXPR interval(const interval & rhs) : l(rhs.l), u(rhs.u) {} - */ + template + SAFE_NUMERIC_CONSTEXPR interval( + const checked_result & lower, + const checked_result & upper + ) : + //l(checked::cast(lower)), + l(lower), + //u(checked::cast(upper)) + u(upper) + {} SAFE_NUMERIC_CONSTEXPR interval() : l(std::numeric_limits::min()), @@ -120,7 +160,10 @@ SAFE_NUMERIC_CONSTEXPR interval operator*(const interval & t, const interv } template -SAFE_NUMERIC_CONSTEXPR interval operator/(const interval & t, const interval & u){ +SAFE_NUMERIC_CONSTEXPR inline interval operator/( + const interval & t, + const interval & u +){ // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic return (u.l <= 0 && u.u >= 0) ? @@ -255,7 +298,7 @@ namespace std { template std::ostream & operator<<(std::ostream & os, const boost::numeric::interval & i){ - os << "[" << i.l << "," << i.u << "]" << std::endl; + os << "[" << i.l << "," << i.u << "]"; return os; } diff --git a/include/safe_base.hpp b/include/safe_base.hpp index b9cf8df..e922742 100644 --- a/include/safe_base.hpp +++ b/include/safe_base.hpp @@ -179,7 +179,6 @@ public: SAFE_NUMERIC_CONSTEXPR safe_base(const checked_result & t) : m_t(static_cast(t)) {} - */ template SAFE_NUMERIC_CONSTEXPR safe_base(const T & t) : m_t(static_cast(t)) @@ -188,6 +187,14 @@ public: E::range_error("Invalid value"); } } + */ + template + SAFE_NUMERIC_CONSTEXPR safe_base(const T & t) : + m_t(t) + { + if(!validate(t)) + E::range_error("Invalid value"); + } template SAFE_NUMERIC_CONSTEXPR safe_base(const safe_base & t); diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index a3154de..292658b 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -485,7 +485,7 @@ typename boost::lazy_enable_if< >, division_result >::type -inline operator/(const T & t, const U & u){ +SAFE_NUMERIC_CONSTEXPR 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 dr; @@ -497,8 +497,11 @@ inline operator/(const T & t, const U & u){ ); typedef typename base_type::type result_base_type; + // typedef print p_result_base_type; typedef typename base_type::type t_base_type; + // typedef print p_t_base_type; typedef typename base_type::type u_base_type; + // typedef print p_u_base_type; // filter out case were overflow cannot occur SAFE_NUMERIC_CONSTEXPR const interval t_interval = { @@ -510,17 +513,25 @@ inline operator/(const T & t, const U & u){ base_value(std::numeric_limits::max()) }; + // when we divide the temporary intervals above, we'll get a new interval + // with the correct range for the result! 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)); + // if no over/under flow or domain error possible + if(r_interval.no_exception() + // and if the denominator cannot contain zero + && (u_interval.l <= 0 && u_interval.u >=0 ) + ) + // we can just invoke the operation and return - even at compile + // time since this is a constexpr ! + return result_type( + static_cast(base_value(t)) + / static_cast(base_value(u)) + ); // otherwise do the division checking for overflow - checked_result r = checked::divide( - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()), + checked_result r = checked::divide( base_value(t), base_value(u) ); @@ -532,6 +543,7 @@ inline operator/(const T & t, const U & u){ ///////////////////////////////////////////////////////////////// // modulus +/* template struct modulus_result { @@ -595,7 +607,7 @@ inline operator%(const T & t, const U & u){ r.template dispatch(); return result_type(static_cast(r)); } - +*/ ///////////////////////////////////////////////////////////////// // comparison diff --git a/include/safe_range.hpp b/include/safe_range.hpp index f31d2a6..32c4cc2 100644 --- a/include/safe_range.hpp +++ b/include/safe_range.hpp @@ -14,7 +14,6 @@ #include // intmax_t, uintmax_t #include // (u)int_t<>::least, exact -#include #include "safe_base.hpp" #include "safe_base_operations.hpp" diff --git a/test/test_automatic.cpp b/test/test_automatic.cpp new file mode 100644 index 0000000..2b8dcfe --- /dev/null +++ b/test/test_automatic.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include "../include/automatic.hpp" + +/* +namespace boost { namespace numeric { + +struct automatic { + template + struct division_result { + static const T t = '0'; + static const U u = 31; + }; +}; + +} // numeric +} // boost +*/ + +template +bool test(){ + int status; + const std::type_info & ti_t = typeid(T); + const std::type_info & ti_u = typeid(U); + std::cout + << "test<" + << abi::__cxa_demangle(ti_t.name(),0,0,&status) + << ',' + << abi::__cxa_demangle(ti_u.name(),0,0,&status) + << ">\n"; + using namespace boost::numeric; + + using division_result_alias = automatic::division_result< + T, U, automatic, void + >; + + const auto t = division_result_alias::t(); + const auto u = division_result_alias::u(); + + std::cout << "t = " << t << '\n'; + std::cout << "u = " << u << '\n'; + + if(std::numeric_limits::is_signed) + std::cout << "r_lower(u()) = " << division_result_alias::r_lower(u) << '\n'; + std::cout << "r_upper(u()) = " << division_result_alias::r_upper(u) << '\n'; + + using max_t = typename division_result_alias::max_t; + std::cout << division_result_alias::template r(t, u) << '\n'; + return true; +} + +#include "test.hpp" + +#define VALUES (8, ( \ + std::int8_t, \ + std::int16_t, \ + std::int32_t, \ + std::int64_t, \ + std::uint8_t, \ + std::uint16_t, \ + std::uint32_t, \ + std::uint64_t \ +)) + +const char *test_result[VALUE_ARRAY_SIZE] = { +// 01234567 +/* 0*/ "........", +/* 1*/ "........", +/* 2*/ "........", +/* 3*/ "........", +/* 4*/ "........", +/* 5*/ "........", +/* 6*/ "........", +/* 7*/ "........" +}; + +#define TEST_IMPL(v1, v2, result) \ + rval &= test(); +/**/ + +#define TESTX(value_index1, value_index2) \ + (std::cout << value_index1 << ',' << value_index2 << ','); \ + TEST_IMPL( \ + BOOST_PP_ARRAY_ELEM(value_index1, VALUES), \ + BOOST_PP_ARRAY_ELEM(value_index2, VALUES), \ + test_result[value_index1][value_index2]() \ + ) +/**/ + +#if 0 +int main(){ + test(); + return 0; +} +#else +int main(){ + bool rval = true; + TEST_EACH_VALUE_PAIR + return ! rval ; +} +#endif + diff --git a/test/test_checked_cast.cpp b/test/test_checked_cast.cpp index d25453f..a9b1d39 100644 --- a/test/test_checked_cast.cpp +++ b/test/test_checked_cast.cpp @@ -24,7 +24,7 @@ bool test_cast( boost::numeric::checked_result r2 = boost::numeric::checked::cast(v1); - if(expected_result == 'x' && r2.is_valid()){ + if(expected_result == 'x' && r2.no_exception()){ std::cout << "failed to detect error in construction " << t2_name << "<-" << t1_name @@ -32,7 +32,7 @@ bool test_cast( boost::numeric::checked::cast(v1); return false; } - if(expected_result == '.' && ! r2.is_valid()){ + if(expected_result == '.' && ! r2.no_exception()){ std::cout << "erroneously emitted error " << t2_name << "<-" << t1_name diff --git a/test/test_checked_divide.cpp b/test/test_checked_divide.cpp new file mode 100644 index 0000000..9173e97 --- /dev/null +++ b/test/test_checked_divide.cpp @@ -0,0 +1,148 @@ +// Copyright (c) 2012 Robert Ramey +// +// 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 +#include +#include // EXIT_SUCCESS +#include + +#include "../include/checked_result.hpp" +#include "../include/checked.hpp" + +template +bool test_checked_divide( + T1 v1, + T2 v2, + const char *av1, + const char *av2, + char expected_result +){ + using namespace boost::numeric; + + std::cout + << "testing " + << av1 << " / " << av2 + << std::endl; + + typedef decltype(T1() / T2()) result_type; + + checked_result result = checked::divide( + std::numeric_limits::min(), + std::numeric_limits::max(), + v1, + v2 + ); + + if(result == exception_type::no_exception + && expected_result != '.'){ + std::cout + << "failed to detect error in division " + << std::hex << "0x" << result << "(" << std::dec << result << ")" + << " != "<< av1 << " / " << av2 + << std::endl; + result = checked::divide( + std::numeric_limits::min(), + std::numeric_limits::max(), + v1, + v2 + ); + return false; + } + else + if(result != exception_type::no_exception + && expected_result != 'x'){ + std::cout + << "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 + ); + return false; + } + return true; // correct result +} + +#include "test.hpp" +#include "test_values.hpp" + +// note: same test matrix as used in test_divide. Here we test all combinations +// safe and unsafe integers. in test_checked we test all combinations of +// integer primitives + +const char *test_division_result[VALUE_ARRAY_SIZE] = { +// 0 0 0 0 +// 01234567012345670123456701234567 +// 01234567890123456789012345678901 +/* 0*/ "................................", +/* 1*/ "................................", +/* 2*/ "........................xxxxxxxx", +/* 3*/ "........................xxxxxxxx", +/* 4*/ ".................................", +/* 5*/ "................................", +/* 6*/ "........................xxxxxxxx", +/* 7*/ "........................xxxxxxxx", + +/* 8*/ "................................", +/* 9*/ "................................", +/*10*/ "...x...x...x............xxxxxxxx", +/*11*/ "........................xxxxxxxx", +/*12*/ "................................", +/*13*/ "................................", +/*14*/ "...x...x...x...x............xxxx", +/*15*/ "............................xxxx", + +// 0 0 0 0 +// 01234567012345670123456701234567 +// 01234567890123456789012345678901 +/*16*/ "................................", +/*17*/ "................................", +/*18*/ "................................", +/*19*/ "................................", +/*20*/ "................................", +/*21*/ "................................", +/*22*/ "................................", +/*23*/ "................................", + +/*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................", +/*31*/ "..xx..xx..xx..xx................" +}; + +#define TEST_IMPL(v1, v2, result) \ + rval &= test_checked_divide( \ + v1, \ + v2, \ + BOOST_PP_STRINGIZE(v1), \ + BOOST_PP_STRINGIZE(v2), \ + result \ + ); +/**/ + +#define TESTX(value_index1, value_index2) \ + (std::cout << value_index1 << ',' << value_index2 << ','); \ + TEST_IMPL( \ + BOOST_PP_ARRAY_ELEM(value_index1, VALUES), \ + BOOST_PP_ARRAY_ELEM(value_index2, VALUES), \ + test_division_result[value_index1][value_index2] \ + ) +/**/ + +int main(int argc, char *argv[]){ + bool rval = true; + + TEST_EACH_VALUE_PAIR + return ! rval ; +} + + diff --git a/test/test_checked_result.cpp b/test/test_checked_result.cpp index e3c4373..37e3c0b 100644 --- a/test/test_checked_result.cpp +++ b/test/test_checked_result.cpp @@ -23,7 +23,7 @@ bool test2(){ checked_result x1(0); assert(0 == x1); checked_result x2(exception_type::overflow_error); - assert(! x2.is_valid()); + assert(! x2.no_exception()); return true; } diff --git a/test/test_divide.cpp b/test/test_divide.cpp index a3ab4fd..ab0a63f 100644 --- a/test/test_divide.cpp +++ b/test/test_divide.cpp @@ -6,9 +6,13 @@ #include #include +#include #include "../include/safe_integer.hpp" +template +using safe_t = boost::numeric::safe; + template bool test_divide( T1 v1, @@ -17,127 +21,159 @@ bool test_divide( const char *av2, char expected_result ){ - std::cout - << "testing " - << av1 << " / " << av2 - << std::endl; + using namespace boost::numeric; + auto unsafe_result = v1 / v2; { - boost::numeric::safe t1 = v1; - // presuming native policy - boost::numeric::safe result; + std::cout << "testing safe<" << av1 << "> / " << av2 << " -> "; + static_assert(is_safe >::value, "safe_t not safe!"); + + safe_t t1 = v1; + + using result_type = decltype(t1 / v2); + result_type result; + + static_assert( + is_safe::value, + "Expression failed to return safe type" + ); try{ result = t1 / v2; - static_assert( - boost::numeric::is_safe::value, - "Expression failed to return safe type" - ); + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == 'x'){ + const std::type_info & ti = typeid(result); + int status; std::cout - << "failed to detect error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " ! = "<< av1 << " / " << av2 + << "*** failed to detect error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' << std::endl; try{ t1 / v2; } catch(std::exception){} + assert(result != unsafe_result); return false; } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ + const std::type_info & ti = typeid(result); + int status; std::cout - << "erroneously detected error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " == "<< av1 << " / " << av2 + << "*** erroneously detected error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' << std::endl; try{ t1 / v2; } catch(std::exception){} + assert(result == unsafe_result); return false; } } } { - boost::numeric::safe t2 = v2; - // presuming native policy - boost::numeric::safe result; + std::cout << "testing " << av1 << " * " << "safe<"<< av2 << "> -> "; + static_assert(is_safe >::value, "safe_t not safe!"); + + safe_t t2 = v2; + + using result_type = decltype(v1 / t2); + result_type result; + + static_assert( + is_safe::value, + "Expression failed to return safe type" + ); try{ result = v1 / t2; - static_assert( - boost::numeric::is_safe::value, - "Expression failed to return safe type" - ); + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == 'x'){ + const std::type_info & ti = typeid(result); + int status; std::cout - << "failed to detect error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " ! = "<< av1 << " / " << av2 + << "*** failed to detect error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' << std::endl; try{ v1 / t2; } catch(std::exception){} + assert(result != unsafe_result); return false; } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ + const std::type_info & ti = typeid(result); + int status; std::cout - << "erroneously detected error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " == "<< av1 << " / " << av2 + << "*** erroneously detected error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' << std::endl; try{ v1 / t2; } catch(std::exception){} + assert(result == unsafe_result); return false; } } } { - boost::numeric::safe t1 = v1; - boost::numeric::safe t2 = v2; + std::cout << "testing safe<" << av1 << "> * safe<" << av2 << "> -> "; + safe_t t1 = v1; + safe_t t2 = v2; - // presuming native policy - boost::numeric::safe result; + using result_type = decltype(t1 / t2); + result_type result; + + static_assert( + is_safe::value, + "Expression failed to return safe type" + ); try{ result = t1 / t2; - - static_assert( - boost::numeric::is_safe::value, - "Expression failed to return safe type" - ); - + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == 'x'){ + const std::type_info & ti = typeid(result); + int status; std::cout - << "failed to detect error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " ! = "<< av1 << " / " << av2 + << "*** failed to detect error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' << std::endl; try{ t1 / t2; } catch(std::exception){} + // assert(result != unsafe_result); return false; } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ + const std::type_info & ti = typeid(result); + int status; std::cout - << "erroneously detected error in division " - << std::hex << result << "(" << std::dec << result << ")" - << " == "<< av1 << " / " << av2 + << "*** erroneously detected error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' << std::endl; try{ t1 / t2; } catch(std::exception){} + // assert(result == unsafe_result); return false; } } @@ -167,11 +203,11 @@ const char *test_division_result[VALUE_ARRAY_SIZE] = { /* 8*/ "................................", /* 9*/ "................................", -/*10*/ "..xx..xx..xx............xxxxxxxx", +/*10*/ "...x...x...x............xxxxxxxx", /*11*/ "........................xxxxxxxx", /*12*/ "................................", /*13*/ "................................", -/*14*/ "..xx..xx..xx..xx............xxxx", +/*14*/ "...x...x...x...x...........xxxxx", /*15*/ "............................xxxx", // 0 0 0 0 @@ -207,7 +243,7 @@ const char *test_division_result[VALUE_ARRAY_SIZE] = { /**/ #define TESTX(value_index1, value_index2) \ - (std::cout << value_index1 << ',' << value_index2 << ','); \ + (std::cout << value_index1 << ',' << value_index2 << '\n'); \ TEST_IMPL( \ BOOST_PP_ARRAY_ELEM(value_index1, VALUES), \ BOOST_PP_ARRAY_ELEM(value_index2, VALUES), \ diff --git a/test/test_divide_auto.cpp b/test/test_divide_auto.cpp new file mode 100644 index 0000000..dabd745 --- /dev/null +++ b/test/test_divide_auto.cpp @@ -0,0 +1,262 @@ +// Copyright (c) 2012 Robert Ramey +// +// 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 +#include +#include + +#include "../include/safe_integer.hpp" +#include "../include/automatic.hpp" + +template +using safe_t = boost::numeric::safe< + T, + boost::numeric::automatic +>; + +template +bool test_divide_auto( + T1 v1, + T2 v2, + const char *av1, + const char *av2, + char expected_result +){ + using namespace boost::numeric; + auto unsafe_result = v1 / v2; + { + std::cout << "testing safe<" << av1 << "> / " << av2 << " -> "; + static_assert(is_safe >::value, "safe_t not safe!"); + + safe_t t1 = v1; + + using result_type = decltype(t1 / v2); + result_type result; + + static_assert( + is_safe::value, + "Expression failed to return safe type" + ); + + try{ + result = t1 / v2; + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == 'x'){ + const std::type_info & ti = typeid(result); + int status; + std::cout + << "*** failed to detect error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << std::endl; + try{ + t1 / v2; + } + catch(std::exception){} + assert(result != unsafe_result); + return false; + } + } + catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == '.'){ + const std::type_info & ti = typeid(result); + int status; + std::cout + << "*** erroneously detected error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << std::endl; + try{ + t1 / v2; + } + catch(std::exception){} + assert(result == unsafe_result); + return false; + } + } + } + { + std::cout << "testing " << av1 << " * " << "safe<"<< av2 << "> -> "; + static_assert(is_safe >::value, "safe_t not safe!"); + + safe_t t2 = v2; + + using result_type = decltype(v1 / t2); + result_type result; + + static_assert( + is_safe::value, + "Expression failed to return safe type" + ); + + try{ + result = v1 / t2; + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == 'x'){ + const std::type_info & ti = typeid(result); + int status; + std::cout + << "*** failed to detect error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << std::endl; + try{ + v1 / t2; + } + catch(std::exception){} + assert(result != unsafe_result); + return false; + } + } + catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == '.'){ + const std::type_info & ti = typeid(result); + int status; + std::cout + << "*** erroneously detected error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << std::endl; + try{ + v1 / t2; + } + catch(std::exception){} + assert(result == unsafe_result); + return false; + } + } + } + { + std::cout << "testing safe<" << av1 << "> * safe<" << av2 << "> -> "; + safe_t t1 = v1; + safe_t t2 = v2; + + using result_type = decltype(t1 / t2); + result_type result; + + static_assert( + is_safe::value, + "Expression failed to return safe type" + ); + + try{ + result = t1 / t2; + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == 'x'){ + const std::type_info & ti = typeid(result); + int status; + std::cout + << "*** failed to detect error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << std::endl; + try{ + t1 / t2; + } + catch(std::exception){} + assert(result != unsafe_result); + return false; + } + } + catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; + if(expected_result == '.'){ + const std::type_info & ti = typeid(result); + int status; + std::cout + << "*** erroneously detected error in division " + << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << std::endl; + try{ + t1 / t2; + } + catch(std::exception){} + assert(result == unsafe_result); + return false; + } + } + } + return true; +} + +#include "test.hpp" +#include "test_values.hpp" + +// note: These tables presume that the the size of an int is 32 bits. +// This should be changed for a different architecture or better yet +// be dynamically adjusted depending on the indicated architecture + +const char *test_division_result[VALUE_ARRAY_SIZE] = { +// 0 0 0 0 +// 01234567012345670123456701234567 +// 01234567890123456789012345678901 +/* 0*/ "................................", +/* 1*/ "................................", +/* 2*/ "................................", +/* 3*/ ".........................xxxxxxx", +/* 4*/ ".................................", +/* 5*/ "................................", +/* 6*/ "........................xxxxxxxx", +/* 7*/ "........................xxxxxxxx", + +/* 8*/ "................................", +/* 9*/ "................................", +/*10*/ "..xx..xx..xx............xxxxxxxx", +/*11*/ "........................xxxxxxxx", +/*12*/ "................................", +/*13*/ "................................", +/*14*/ "..xx..xx..xx..xx............xxxx", +/*15*/ "............................xxxx", + +// 0 0 0 0 +// 01234567012345670123456701234567 +// 01234567890123456789012345678901 +/*16*/ "................................", +/*17*/ "................................", +/*18*/ "................................", +/*19*/ "................................", +/*20*/ "................................", +/*21*/ "................................", +/*22*/ "................................", +/*23*/ "................................", + +/*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................", +/*31*/ "..xx..xx..xx..xx................" +}; + +#define TEST_IMPL(v1, v2, result) \ + rval &= test_divide_auto( \ + v1, \ + v2, \ + BOOST_PP_STRINGIZE(v1), \ + BOOST_PP_STRINGIZE(v2), \ + result \ + ); +/**/ + +#define TESTX(value_index1, value_index2) \ + (std::cout << value_index1 << ',' << value_index2 << ','); \ + TEST_IMPL( \ + BOOST_PP_ARRAY_ELEM(value_index1, VALUES), \ + BOOST_PP_ARRAY_ELEM(value_index2, VALUES), \ + test_division_result[value_index1][value_index2] \ + ) +/**/ + +int main(int argc, char * argv[]){ + bool rval = true; + TEST_EACH_VALUE_PAIR + return ! rval ; +} diff --git a/test/test_interval.cpp b/test/test_interval.cpp index 97d39c2..9dba173 100644 --- a/test/test_interval.cpp +++ b/test/test_interval.cpp @@ -1,12 +1,15 @@ #include #include #include +#include +#include #include "../include/interval.hpp" #include bool test1(){ using namespace boost::numeric; + std::cout << "test1" << std::endl; interval x = {-64, 63}; std::cout << "x = " << x; interval y(-128, 126); @@ -19,6 +22,7 @@ bool test1(){ bool test2(){ using namespace boost::numeric; + std::cout << "test2" << std::endl; boost::numeric::interval x = {-64, 63}; std::cout << "x = " << x; std::cout << std::boolalpha << "(x == x) = " << (x == x) << std::endl; @@ -27,6 +31,7 @@ bool test2(){ bool test3(){ using namespace boost::numeric; + std::cout << "test3" << std::endl; interval t; std::cout << "t = " << t; interval u; @@ -37,11 +42,143 @@ bool test3(){ return true; } +#include + +namespace test4 { + using namespace boost::numeric; + using max_t = std::intmax_t; + // typedef typename print::type p_max_t; + + template + SAFE_NUMERIC_CONSTEXPR static const interval r_upper(const interval & t){ + static_assert( + std::is_literal_type< interval >::value, + "interval is not literal type" + ); + return interval( + max(checked_result(1), t.l), + t.u + ); + } + template + SAFE_NUMERIC_CONSTEXPR static const interval r_lower(const interval & t){ + static_assert( + std::is_literal_type< interval >::value, + "interval is not literal type" + ); + return interval( + t.l, + min(checked_result(-1), t.u) + ); + } + + template + SAFE_NUMERIC_CONSTEXPR static const interval r( + const interval & t, + const interval & u + ){ + if(std::numeric_limits::is_signed){ + if(u.l > 0 || u.u < 0){ + auto retval = operator/(t, u); + // typedef print p_retval; + return retval; + //return operator/(t, u); + } + else{ + auto lower = operator/(t,r_lower(u)); + // typedef print p_lower; + auto upper = operator/(t,r_upper(u)); + // typedef print p_lower; + return + interval< max_t>( + min(lower.l, upper.l), + max(lower.u, upper.u) + ); + /* + */ + // return t; + } + } + else{ // u is unsigned + if(u.l > 0) + return operator/(t, u); + else + return operator/(t, r_upper(u)) ; + }; + }; + + bool test1(){ + using namespace boost::numeric; + int status; + std::cout << "test4::test1" << std::endl; + const interval t; + std::cout + << abi::__cxa_demangle(typeid(t).name(),0,0,&status) + << " t = " + << t; + const interval u; + std::cout + << abi::__cxa_demangle(typeid(u).name(),0,0,&status) + << " u = " + << u; + const interval rx = r(t, u); + std::cout + << abi::__cxa_demangle(typeid(rx).name(),0,0,&status) + << " rx = " + << rx; + return true; + } + bool test2(){ + using namespace boost::numeric; + int status; + std::cout << "test4::test2" << std::endl; + const interval t; + std::cout + << abi::__cxa_demangle(typeid(t).name(),0,0,&status) + << " t = " + << t; + const interval u; + std::cout + << abi::__cxa_demangle(typeid(u).name(),0,0,&status) + << " u = " + << u; + const interval rx = r(t, u); + std::cout + << abi::__cxa_demangle(typeid(rx).name(),0,0,&status) + << " rx = " + << rx; + return true; + } + bool test3(){ + using namespace boost::numeric; + int status; + std::cout << "test4::test2" << std::endl; + const interval t; + std::cout + << abi::__cxa_demangle(typeid(t).name(),0,0,&status) + << " t = " + << t; + const interval u; + std::cout + << abi::__cxa_demangle(typeid(u).name(),0,0,&status) + << " u = " + << u; + const interval rx = r(t, u); + std::cout + << abi::__cxa_demangle(typeid(rx).name(),0,0,&status) + << " rx = " + << rx; + return true; + } +}; int main(){ return ( test1() && test2() && - test3() + test3() && + test4::test1() && + test4::test2() && + test4::test3() ) ? EXIT_SUCCESS : EXIT_FAILURE; } \ No newline at end of file diff --git a/test/test_multiply_auto.cpp b/test/test_multiply_auto.cpp index c7b1453..3cd1cd1 100644 --- a/test/test_multiply_auto.cpp +++ b/test/test_multiply_auto.cpp @@ -26,9 +26,9 @@ bool test_multiply_auto( char expected_result ){ using namespace boost::numeric; - auto unsafe_result = v1 + v2; + auto unsafe_result = v1 * v2; { - std::cout << "testing safe<" << av1 << "> * " << av2 << " -> "; + std::cout << "testing safe<" << av1 << "> * " << av2 << " -> "; static_assert(is_safe >::value, "safe_t not safe!"); safe_t t1 = v1; diff --git a/test/test_z.cpp b/test/test_z.cpp index d4a1bea..889298a 100644 --- a/test/test_z.cpp +++ b/test/test_z.cpp @@ -1,24 +1,120 @@ +#if 0 + +#include +#include + +template +struct division_result { + static const T t = '0'; +}; + +template +int test(){ + std::cout << "t = " << division_result::t << std::endl; + return 0; +} +int main(){ + test(); + return 0; +} + +#endif + +#include +#include +#include #include "../include/safe_integer.hpp" -#include "../include/native.hpp" +#include "../include/automatic.hpp" + +template +using safe_t = boost::numeric::safe< + T, + boost::numeric::automatic +>; int main(){ using namespace boost::numeric; -/* - - safe x = 0xffff; - uint16_t y = 0xffff; - - typedef boost::numeric::native::multiplication_result< - decltype(x), - decltype(y), - native, - void - >::type result_type; - print p_rt; - print p_yy; - - x * y; -*/ + { + const safe_t t = 10; + const std::uint8_t u = 2; + auto z = t / u; + std::cout << z << std::endl; + } + { + const safe_t t = std::numeric_limits>::max(); + const std::int8_t u = -128; + try { + auto z = t / u; + std::cout << z << std::endl; + } + catch(const std::exception & e){} + } + { + const safe_t t = std::numeric_limits>::max(); + const std::uint8_t u = 0; + try { + auto z = t / u; + std::cout << z << std::endl; + } + catch(const std::exception & e){} + } + { + const safe_t t = 10; + const std::uint8_t u = 2; + auto z = t / u; + std::cout << z << std::endl; + } + { + const safe_t t = 10; + const std::int8_t u = -1; + auto z = t / u; + std::cout << z << std::endl; + } + { + const safe_t t = -10; + const std::uint8_t u = 2; + auto z = t / u; + std::cout << z << std::endl; + } + { + const safe_t t = 10; + const std::int8_t u = -1; + auto z = t / u; + std::cout << z << std::endl; + } + { + const safe_t t = 10; + const std::int8_t u = -1; + auto z = t / u; + std::cout << z << std::endl; + } + { + const safe_t t = 10; + const std::int8_t u = -1; + auto z = t / u; + std::cout << z << std::endl; + } + { + safe_t t = 1; + std::int32_t u = 0x7fffffff; + auto z = t / u; + std::cout << z << std::endl; + } + { + const safe_t t = 10; + const std::int64_t u = 2; + auto z = t / u; + std::cout << z << std::endl; + } + /* + */ + { + const safe_t t = 10; + const std::uint64_t u = 2; + auto z = t / u; + std::cout << z << std::endl; + } return 0; } +