diff --git a/examples/example91.cpp b/examples/example91.cpp index d4b4106..37920f4 100644 --- a/examples/example91.cpp +++ b/examples/example91.cpp @@ -11,8 +11,6 @@ #include #include "../include/cpp.hpp" -#include "../include/automatic.hpp" -#include "../include/exception.hpp" #include "../include/safe_integer.hpp" #include "../include/safe_range.hpp" diff --git a/include/automatic.hpp b/include/automatic.hpp index f94abc2..b125dde 100644 --- a/include/automatic.hpp +++ b/include/automatic.hpp @@ -43,8 +43,8 @@ struct automatic { template using result_type = - typename boost::mpl::if_< - std::is_signed, + typename boost::mpl::if_c< + std::numeric_limits::is_signed, defer_stored_signed_lazily, defer_stored_unsigned_lazily >::type; @@ -73,20 +73,17 @@ struct automatic { base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> + constexpr static const interval> r_interval = add(t_interval, u_interval); - constexpr static const interval - result_interval = r_interval.no_exception() ? - static_cast>(r_interval) - : - interval{} - ; - using type = typename result_type< temp_base_type, - result_interval.l, - result_interval.u + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u) >::type; }; @@ -105,20 +102,17 @@ struct automatic { base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> + constexpr static const interval> r_interval = subtract(t_interval, u_interval); - - constexpr static const interval result_interval = - r_interval.no_exception() ? - static_cast>(r_interval) - : - interval{} - ; using type = typename result_type< temp_base_type, - result_interval.l, - result_interval.u + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u) >::type; }; @@ -145,20 +139,17 @@ struct automatic { base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> + constexpr static const interval> r_interval = multiply(t_interval, u_interval); - constexpr static const interval result_interval = - r_interval.no_exception() ? - static_cast>(r_interval) - : - interval{} - ; - using type = typename result_type< temp_base_type, - result_interval.l, - result_interval.u + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u) >::type; }; @@ -185,34 +176,32 @@ struct automatic { base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> - r_interval = divide_nz(t_interval, u_interval); + constexpr static const interval> + r_interval = divide(t_interval, u_interval); + constexpr static temp_base_type lower_helper = + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l); - constexpr static const interval result_interval { - r_interval.no_exception() ? - static_cast>(r_interval) - : - interval{} - }; + constexpr static temp_base_type lower = + lower_helper < 0 && + lower_helper != std::numeric_limits::min() + ? lower_helper - 1 + : lower_helper; + + constexpr static temp_base_type upper = + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u); using type = typename result_type< temp_base_type, - result_interval.l, - result_interval.u + lower, + upper >::type; }; - // forward to correct divide implementation - template - checked_result - static constexpr divide( - const T & t, - const U & u - ){ - return checked::divide_automatic(t, u); - } - /////////////////////////////////////////////////////////////////////// template struct modulus_result { @@ -236,21 +225,17 @@ struct automatic { base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> - r_interval = modulus_nz(t_interval, u_interval); - - - constexpr static const interval result_interval { - r_interval.no_exception() ? - static_cast>(r_interval) - : - interval{} - }; + constexpr static const interval> + r_interval = modulus(t_interval, u_interval); using type = typename result_type< temp_base_type, - result_interval.l, - result_interval.u + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u) >::type; }; @@ -266,7 +251,6 @@ struct automatic { /////////////////////////////////////////////////////////////////////// // shift operations - template struct left_shift_result { using t_base_type = typename base_type::type; @@ -276,43 +260,28 @@ struct automatic { base_value(std::numeric_limits::max()) }; + using temp_base_type = typename boost::mpl::if_c< + std::numeric_limits::is_signed, + std::intmax_t, + std::uintmax_t + >::type; + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> r_interval = - left_shift(t_interval, u_interval); - - constexpr static const interval result_interval = - r_interval.no_exception() ? - static_cast>(r_interval) : - interval() - ; - - constexpr static const std::uintmax_t upper_bound = - r_interval.no_exception() ? - static_cast>(r_interval).u : - std::numeric_limits::max(); - - constexpr static const std::uintmax_t lower_bound = - r_interval.no_exception() ? - static_cast>(r_interval).l : - std::numeric_limits::min(); - - using r_base_type = typename boost::mpl::if_c< - r_interval.no_exception(), - typename boost::numeric::unsigned_stored_type< - lower_bound, - upper_bound - >, - std::uintmax_t - >::type; + constexpr static const interval> r_interval = + left_shift(t_interval, u_interval); using type = typename result_type< - r_base_type, - lower_bound, - upper_bound + temp_base_type, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u) >::type; }; @@ -320,14 +289,34 @@ struct automatic { template struct right_shift_result { using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; constexpr static const interval t_interval{ - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) }; + + using temp_base_type = typename boost::mpl::if_c< + std::numeric_limits::is_signed, + std::intmax_t, + std::uintmax_t + >::type; + + constexpr static const interval u_interval{ + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + constexpr static const interval> r_interval = + right_shift(t_interval, u_interval); + using type = typename result_type< - T, - t_interval.l, - t_interval.u + temp_base_type, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u) >::type; }; diff --git a/include/checked.hpp b/include/checked.hpp index 23b9438..6e3e8ea 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -52,13 +52,13 @@ namespace detail { return t > std::numeric_limits::max() ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "converted signed value too large" ) : t < std::numeric_limits::min() ? checked_result( - exception_type::overflow_error, + exception_type::negative_overflow_error, "converted signed value too small" ) : @@ -75,7 +75,7 @@ namespace detail { return t > std::numeric_limits::max() ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "converted unsigned value too large" ) : @@ -92,7 +92,7 @@ namespace detail { return t > std::numeric_limits::max() ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "converted unsigned value too large" ) : @@ -113,7 +113,7 @@ namespace detail { : t > std::numeric_limits::max() ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "converted signed value too large" ) : @@ -183,7 +183,7 @@ namespace detail { // INT30-C. Ensure that unsigned integer operations do not wrap std::numeric_limits::max() - u < t ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "addition overflow" ) : @@ -204,11 +204,16 @@ namespace detail { ) { return // INT32-C. Ensure that operations on signed integers do not result in overflow - ((u > 0) && (t > (std::numeric_limits::max() - u))) - || ((u < 0) && (t < (std::numeric_limits::min() - u))) ? + ((u > 0) && (t > (std::numeric_limits::max() - u))) ? checked_result( - exception_type::overflow_error, - "addition overflow" + exception_type::positive_overflow_error, + "addition result too large" + ) + : + ((u < 0) && (t < (std::numeric_limits::min() - u))) ? + checked_result( + exception_type::negative_overflow_error, + "addition result too low" ) : checked_result(t + u) @@ -264,8 +269,8 @@ namespace detail { return t < u ? checked_result( - exception_type::overflow_error, - "subtraction overflow" + exception_type::range_error, + "subtraction result cannot be negative" ) : checked_result(t - u) @@ -285,11 +290,16 @@ namespace detail { ) { // INT32-C return // INT32-C. Ensure that operations on signed integers do not result in overflow - ((u > 0) && (t < (std::numeric_limits::min() + u))) - || ((u < 0) && (t > (std::numeric_limits::max() + u))) ? + ((u > 0) && (t < (std::numeric_limits::min() + u))) ? checked_result( - exception_type::overflow_error, - "subtraction overflow" + exception_type::positive_overflow_error, + "subtraction result overflows result type" + ) + : + ((u < 0) && (t > (std::numeric_limits::max() + u))) ? + checked_result( + exception_type::negative_overflow_error, + "subtraction result overflows result type" ) : checked_result(t - u) @@ -349,7 +359,7 @@ namespace detail { static_cast(t) * static_cast(u) > std::numeric_limits::max() ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "multiplication overflow" ) : @@ -370,7 +380,7 @@ namespace detail { return u > 0 && t > std::numeric_limits::max() / u ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "multiplication overflow" ) : @@ -399,7 +409,7 @@ namespace detail { > static_cast(std::numeric_limits::max()) ) ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "multiplication overflow" ) : @@ -408,7 +418,7 @@ namespace detail { < static_cast(std::numeric_limits::min()) ) ? checked_result( - exception_type::overflow_error, + exception_type::negative_overflow_error, "multiplication overflow" ) : @@ -429,7 +439,7 @@ namespace detail { u > 0 ? t > std::numeric_limits::max() / u ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "multiplication overflow" ) : @@ -437,7 +447,7 @@ namespace detail { : // u <= 0 u < std::numeric_limits::min() / t ? checked_result( - exception_type::overflow_error, + exception_type::negative_overflow_error, "multiplication overflow" ) : @@ -446,7 +456,7 @@ namespace detail { u > 0 ? t < std::numeric_limits::min() / u ? checked_result( - exception_type::overflow_error, + exception_type::negative_overflow_error, "multiplication overflow" ) : @@ -454,7 +464,7 @@ namespace detail { : // u <= 0 t != 0 && u < std::numeric_limits::max() / t ? checked_result( - exception_type::overflow_error, + exception_type::positive_overflow_error, "multiplication overflow" ) : @@ -516,6 +526,7 @@ namespace detail { } } // detail +// note that we presume that the size of R >= size of T template checked_result constexpr divide( @@ -533,66 +544,12 @@ constexpr divide( if(tx.exception() || ux.exception()) return checked_result( - exception_type::overflow_error, + exception_type::domain_error, "failure converting argument types" ); return detail::divide(tx, ux); } -namespace detail_automatic { - - template - typename boost::enable_if_c< - ! std::numeric_limits::is_signed, - checked_result - >::type - 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 - constexpr divide( - const T & t, - const U & u - ){ - if(u == -1 && t == std::numeric_limits::min()) - return - checked_result( - exception_type::domain_error, - "result cannot be represented" - ) - ; - checked_result tx = checked::cast(t); - if(! tx.no_exception()) - return tx; - return static_cast(tx) / u; - - } - -} // detail_automatic - -template -checked_result -constexpr divide_automatic( - const T & t, - const U & u -){ - if(u == 0){ - return checked_result( - exception_type::domain_error, - "divide by zero" - ); - } - return detail_automatic::divide(t, u); -} - //////////////////////////////// // safe modulus on unsafe types @@ -620,8 +577,8 @@ constexpr modulus( "denominator is zero" ); - // why to we need abs here? the sign of the modulus is the sign - // consider -128 % -1 The result of this operation should be -1 + // why to we need abs here? the sign of the modulus is the sign of the + // dividend. Consider -128 % -1 The result of this operation should be -1 // but if I use t % u the x86 hardware uses the divide instruction // capturing the modulus as a side effect. When it does this, it // invokes the operation -128 / -1 -> 128 which overflows a signed type @@ -633,10 +590,102 @@ constexpr modulus( /////////////////////////////////// // shift operations -// left shift - namespace detail { +/* +From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious +Find the log base 2 of an integer with a lookup table + + static const char LogTable256[256] = + { + #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), + LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) + }; + + unsigned int v; // 32-bit word to find the log of + unsigned r; // r will be lg(v) + register unsigned int t, tt; // temporaries + + if (tt = v >> 16) + { + r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]; + } + else + { + r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v]; + } + +The lookup table method takes only about 7 operations to find the log of a 32-bit value. +If extended for 64-bit quantities, it would take roughly 9 operations. Another operation +can be trimmed off by using four tables, with the possible additions incorporated into each. +Using int table elements may be faster, depending on your architecture. +*/ + +// I've "improved" the above and recast as C++ code which depends upon +// the optimizer to minimize the operations. This should result in +// nine operations to calculate the position of the highest order +// bit in a 64 bit number. RR + +constexpr unsigned int log2(const std::uint8_t & t){ + constexpr const char LogTable256[256] = { + #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), + LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) + }; + return LogTable256[t]; +} +constexpr unsigned int log2(const std::uint16_t & t){ + const std::uint8_t upper_half = (t >> 8); + return upper_half == 0 + ? log2(static_cast(t)) + : 8 + log2(upper_half); +} +constexpr unsigned int log2(const std::uint32_t & t){ + const std::uint16_t upper_half = (t >> 16); + return upper_half == 0 + ? log2(static_cast(t)) + : 16 + log2(upper_half); +} +constexpr unsigned int log2(const std::uint64_t & t){ + const std::uint32_t upper_half = (t >> 32); + return upper_half == 0 + ? log2(static_cast(t)) + : 32 + log2(upper_half); +} + +#if 0 +// todo - optimize for gcc to exploit builtin +/* for gcc compilers +int __builtin_clz (unsigned int x) + Returns the number of leading 0-bits in x, starting at the + most significant bit position. If x is 0, the result is undefined. +*/ + +#ifndef __has_feature // Optional of course. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +template +constexpr unsigned int leading_zeros(const T & t){ + if(0 == t) + return 0; + #if __has_feature(builtin_clz) + return __builtin_clz(t); + #else + #endif +} +#endif + +template +constexpr unsigned int significant_bits(const T & t){ + return log2( + static_cast::type>(t) + ) + 1; +} + // INT34-C C++ // standard paragraph 5.8 / 2 @@ -654,41 +703,23 @@ constexpr checked_left_shift( ) noexcept { // the value of the result is E1 x 2^E2, reduced modulo one more than // the maximum value representable in the result type. - /* - if(u >= std::numeric_limits::digits){ - return cast(0); - } - */ + // note: we are intentionally varying from the standards language here. // a safe is meant to be value preserving. That is t << u should // equal an arithmetically correct value or fail in some visible manner. // So we're going to fail if are shift loses higher order bits. - // + // note: there is a good argument for following exactly the standards // language above. Reasonable people can disagree. - if(u == 0){ - // behavior is undefined - return checked_result( - exception_type::domain_error, - "shifting all bits off the left is undefined behavior" - ); - } - - const int max_shift = std::numeric_limits::digits - u; - - if(max_shift < 0 - || u >= max_shift - || t >= (cast(1) << max_shift) - ){ + if(u > std::numeric_limits::digits - significant_bits(t)){ // behavior is undefined return checked_result( exception_type::domain_error, "shifting more bits than available is undefined behavior" ); } - - return cast(t) << u; + return t << u; } template @@ -701,29 +732,20 @@ constexpr checked_left_shift( const T & t, const U & u ) { - // and E1 x 2^E2 is representable in the corresponding - // unsigned type of the result type, - - const int max_shift = std::numeric_limits::digits - u - 1; - - if(max_shift < 0 - || u >= max_shift - || t >= (cast(1) << max_shift) - ){ - // behavior is undefined - return checked_result( - exception_type::domain_error, - "shifting more bits than available is undefined behavior" - ); - } if(t < 0){ return checked_result( exception_type::domain_error, "shifting a negative value is undefined behavior" ); } - // shift and convert the resulting value to the result type - return cast(t) << u; + + // and E1 x 2^E2 is representable in the corresponding + // unsigned type of the result type, + + return checked_left_shift( + static_cast::type>(t), + u + ); } } // detail @@ -733,10 +755,12 @@ constexpr checked_result left_shift( const T & t, const U & u ){ - // INT13-C Note: - // on all known modern machines with will yield the correct - // result. I'll err on the conservative side and trap this - // as undefined behavior. But someone is going to complain + // INT34-C - Do not shift an expression by a negative number of bits + // C++ Standard standard paragraph 5.8.3 Doesn't excactly say this. + // It says the operation is defined as E1 / 2^E2 which IS defined when + // E2 is negative. + // I'm going to err on the side of concervativism dis-allowing negative + // shift counts. if(u < 0){ return checked_result( exception_type::domain_error, @@ -744,15 +768,13 @@ constexpr checked_result left_shift( ); } if(t == 0) - return cast(0); + return cast(t); return detail::checked_left_shift(t, u); } // right shift namespace detail { -// standard paragraph 5.8 / 3 -// The value of E1 >> E2 is E1 right-shifted E2 bit positions; template typename std::enable_if< // If E1 has an unsigned type @@ -763,11 +785,6 @@ constexpr checked_right_shift( const T & t, const U & u ) noexcept { - // the value of the result is E1 / 2^E2, reduced modulo one more than - // the maximum value representable in the result type. - if(u > std::numeric_limits::digits){ - return cast(0); - } return cast(t >> u); } @@ -781,22 +798,19 @@ constexpr checked_right_shift( const T & t, const U & u ) { - // and a non-negative value + // INT13C - Use bitwise operators only on unsigned integers if(t < 0){ + // note that the C++ standard considers this case is "implemenation + // defined" rather than "undefined". We'll err on the side of + // caution and disallow this. return checked_result( exception_type::domain_error, "shifting a negative value is undefined behavior" ); } - if(u >= std::numeric_limits::digits){ - return checked_result( - exception_type::domain_error, - "shifting more bits than argument size is an error" - ); - } // the value is the integral part of E1 / 2^E2, - return cast(t) >> u; + return cast(t >> u); } } // detail @@ -807,13 +821,36 @@ constexpr checked_result right_shift( const T & t, const U & u ) { - // on all known modern machines with will yield the correct - // result. I'll err on the conservative side and trap this - // as undefined behavior. But someone is going to complain + // INT34-C - Do not shift an expression by a negative number of bits + // C++ Standard standard paragraph 5.8.3 Doesn't excactly say this. + // It says the operation is defined as E1 / 2^E2 which IS defined when + // E2 is negative. + // I'm going to err on the side of concervativism dis-allowing negative + // shift counts. if(u < 0){ + // note that the C++ standard considers this case is "implemenation + // defined" rather than "undefined". We'll err on the side of + // caution and dis-allow this. return checked_result( exception_type::domain_error, - "shifting negative amount is undefined behavior" + "shifting a negative value is undefined behavior" + ); + } + // standard paragraph 5.8 / 3 + // Again, C++ Standard standard paragraph 5.8.3 doesn't seem to disallow + // shifts count > argument width. + + // Also, my Clang compiler traps this an an error when used in a constexpr + // at compile time. + + // Given all of the above, we're going to disallow shifting which exceeds + // the width of the argument. + else + if(u > std::numeric_limits::digits){ + // behavior is undefined + return checked_result( + exception_type::domain_error, + "shifting more bits than available is undefined behavior" ); } if(t == 0) @@ -828,7 +865,7 @@ namespace detail { // INT13-C Note: We don't enforce recommendation as acually written // as it would break too many programs. Specifically, we permit signed // integer operand but require that it not be negative. This we can only - // enforce at runtime. + // enforced at runtime. template typename boost::enable_if< typename std::is_signed, diff --git a/include/checked_result.hpp b/include/checked_result.hpp index e579ac4..977aef0 100644 --- a/include/checked_result.hpp +++ b/include/checked_result.hpp @@ -33,7 +33,8 @@ struct checked_result { char const * m_msg; }; // constructors - use default copy constructor - + // checked_result(const checked_result &) = default; + // don't permit construction without initial value; checked_result() = delete; @@ -41,7 +42,6 @@ struct checked_result { m_e(exception_type::no_exception), m_r(r) {} - constexpr /*explicit*/ checked_result( exception_type e, const char * msg @@ -57,7 +57,7 @@ struct checked_result { //assert(no_exception()); return m_r; } - + constexpr operator const char *() const { assert(! no_exception()); return m_msg; @@ -105,6 +105,23 @@ struct checked_result { } }; +template +constexpr bool operator==(const checked_result &lhs, const exception_type & rhs){ + return (! lhs.exception()) ? false : lhs.m_e == rhs; +} +template +constexpr bool operator==(const exception_type & lhs, const checked_result &rhs){ + return (! rhs.exception()) ? false : lhs = rhs.m_e; +} +template +constexpr bool operator!=(const checked_result &lhs, const exception_type & rhs){ + return ! (lhs == rhs); +} +template +constexpr bool operator!=(const exception_type & lhs, const checked_result &rhs){ + return ! (lhs == rhs); +} + template void dispatch(const checked_result & cr){ @@ -145,9 +162,9 @@ inline std::basic_ostream & operator<<( return os; } -template +template inline std::basic_ostream & operator<<( - std::basic_ostream os, + std::basic_ostream & os, const boost::numeric::checked_result & r ){ if(r.no_exception()) @@ -157,9 +174,9 @@ inline std::basic_ostream & operator<<( return os; } -template +template inline std::basic_ostream & operator<<( - std::basic_ostream os, + std::basic_ostream & os, const boost::numeric::checked_result & r ){ if(r.no_exception()) diff --git a/include/concept/promotion_policy.hpp b/include/concept/promotion_policy.hpp index 39b17c5..c4babea 100644 --- a/include/concept/promotion_policy.hpp +++ b/include/concept/promotion_policy.hpp @@ -12,8 +12,6 @@ // 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 { @@ -31,14 +29,6 @@ struct PromotionPolicy { using baw_type = typename PP::template bitwise_and_result; using bow_type = typename PP::template bitwise_or_result; using bxw_type = typename PP::template bitwise_xor_result; - - checked_result::type> divide(){ - return PP::template divide::type>(0, 0); - } - checked_result::type> modulus(){ - return PP::template modulus::type>(0, 0); - } - }; } // numeric diff --git a/include/cpp.hpp b/include/cpp.hpp index 358620e..538a4cc 100755 --- a/include/cpp.hpp +++ b/include/cpp.hpp @@ -22,9 +22,9 @@ #include // integer type selection #include -#include "utility.hpp" // rank #include "safe_common.hpp" #include "checked.hpp" +#include "checked_result.hpp" namespace boost { namespace numeric { @@ -50,6 +50,26 @@ struct cpp { using local_long_type = typename boost::int_t::exact; using local_long_long_type = typename boost::int_t::exact; + template + using rank = + typename boost::mpl::if_c< + std::is_same::type>::value, + std::integral_constant, + typename boost::mpl::if_c< + std::is_same::type>::value, + std::integral_constant, + typename boost::mpl::if_c< + std::is_same::type>::value, + std::integral_constant, + typename boost::mpl::if_c< + std::is_same::type>::value, + std::integral_constant, + typename boost::mpl::if_c< + std::is_same::type>::value, + std::integral_constant, + std::integral_constant // catch all - never promote integral + >::type >::type >::type >::type >::type; + // section 4.5 integral promotions template using integral_promotion = typename boost::mpl::if_c< @@ -58,7 +78,7 @@ struct cpp { T >::type; - // convert smaller of two types to the size of the larger + // convert smaller of two types to the size of the larger template using higher_ranked_type = typename boost::mpl::if_c< (rank::value < rank::value), @@ -82,7 +102,7 @@ struct cpp { T >::type; - // section 5 - usual arithmetic conversions + // section 5 clause 11 - usual arithmetic conversions template using usual_arithmetic_conversions = // clause 0 - if both operands have the same type @@ -136,6 +156,7 @@ struct cpp { struct division_result { using type = result_type; }; +/* // forward to correct divide implementation template checked_result @@ -145,11 +166,12 @@ struct cpp { ){ return checked::divide(t, u); } - +*/ template struct modulus_result { using type = result_type; }; +/* // forward to correct modulus implementation template checked_result @@ -159,6 +181,7 @@ struct cpp { ){ return checked::modulus(t, u); } +*/ template struct left_shift_result { using type = result_type; diff --git a/include/exception.hpp b/include/exception.hpp index 1fdc24b..22e628a 100644 --- a/include/exception.hpp +++ b/include/exception.hpp @@ -20,9 +20,12 @@ namespace boost { namespace numeric { +// in spite of the similarity, this list is distinct from the exceptions +// listed in documentation for std::exception. enum class exception_type { no_exception, - overflow_error, + positive_overflow_error, + negative_overflow_error, underflow_error, range_error, domain_error, @@ -33,7 +36,8 @@ template void dispatch(const exception_type & e, char const * msg){ switch(e){ - case exception_type::overflow_error: + case exception_type::positive_overflow_error: + case exception_type::negative_overflow_error: EP::overflow_error(msg); break; case exception_type::underflow_error: diff --git a/include/interval.hpp b/include/interval.hpp index ef96636..fb92bb0 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -19,6 +19,7 @@ #include #include +#include #include "utility.hpp" // log #include "checked_result.hpp" @@ -98,77 +99,86 @@ constexpr interval::interval() : namespace detail { -template -constexpr checked_result> failed_result( - exception_type::domain_error, - "indefinite interval" -); - -// create constexpr versions of stl algorthms which are not (yet) -// constexpr. -template -constexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate q){ - for (; first != last; ++first) { - if (q(*first)) { - return first; +template +constexpr interval +minmax(const std::initializer_list & l){ + using namespace boost::numeric; + T minimum{exception_type::positive_overflow_error, ""}; + T maximum{exception_type::negative_overflow_error, ""}; + // note: we can't use for_each and a lambda because neither of these are + // constexpr + for( + typename std::initializer_list::const_iterator i = l.begin(); + i != l.end(); + ++i + ){ + // std::cout << *i << ','; + // std::cout.flush(); + if(minimum != exception_type::negative_overflow_error){ + // if it corresponds to the lowest value + if(*i == exception_type::negative_overflow_error){ + // initialize the minimum + minimum = *i; + } + // skip any other exceptions + else + if(! (*i).exception()){ + if(minimum.exception() + || (*i).m_r < minimum.m_r){ + minimum = *i; + } + } + } + if(maximum != exception_type::positive_overflow_error){ + // if it corresponds to the highest value + if(*i == exception_type::positive_overflow_error){ + // initialize the maximum + maximum = *i; + } + // skip any other exceptions + else + if(! (*i).exception()){ + if(maximum.exception() + || (*i).m_r > maximum.m_r){ + maximum = *i; + } + } } } - return last; -} - -// helper function used below to select the minimum and maximum value -// from a list of values -template -constexpr checked_result> minmax( - const std::initializer_list> & acr -){ - typename std::initializer_list>::const_iterator const e = find_if( - acr.begin(), - acr.end(), - exception - ); - return (acr.end() == e) ? - checked_result>( - interval(std::minmax(acr, & safe_compare::less_than)) - ) - : - failed_result// throw assert_failure([]{assert(!"input not in range");}) - ; + return interval{minimum, maximum}; } } // detail template -constexpr checked_result> add( +constexpr interval> add( const interval & t, const interval & u ){ // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - checked_result lower = checked::add(static_cast(t.l), static_cast(u.l)); - if(! lower.no_exception()) - return detail::failed_result; - checked_result upper = checked::add(static_cast(t.u), static_cast(u.u)); - if(! upper.no_exception()) - return detail::failed_result; - return interval(lower, upper); + const checked_result lower = checked::add(t.l, u.l); + const checked_result upper = checked::add(t.u, u.u); + return interval>(lower, upper); } template -constexpr checked_result> subtract(const interval & t, const interval & u){ +constexpr interval> subtract( + const interval & t, + const interval & u +){ // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - checked_result lower = checked::subtract(static_cast(t.l), static_cast(u.u)); - if(! lower.no_exception()) - return detail::failed_result; - checked_result upper = checked::subtract(static_cast(t.u), static_cast(u.l)); - if(! upper.no_exception()) - return detail::failed_result; - return interval(lower, upper); + const checked_result lower = checked::subtract(t.l, u.u); + const checked_result upper = checked::subtract(t.u, u.l); + return interval>(lower, upper); } template -constexpr checked_result> multiply(const interval & t, const interval & u){ +constexpr interval> multiply( + const interval & t, + const interval & u +){ // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - return detail::minmax( + return detail::minmax>( std::initializer_list> { checked::multiply(t.l, u.l), checked::multiply(t.l, u.u), @@ -178,139 +188,113 @@ constexpr checked_result> multiply(const interval & t, const inte ); } -// divide two intervals. BUT don't consider the possibility that the -// denominator might contain a zero. +// divide two intervals. This will give a new range of the quotient. But +// it doesn't consider the possibility that the divisor is zero. This will +// have to be considered separately. template -constexpr inline checked_result> divide_nz( +constexpr inline interval> divide( const interval & t, const interval & u ){ - // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - return detail::minmax( (u.u < 0 || u.l > 0) ? - std::initializer_list> { - checked::divide(t.l, u.l), - checked::divide(t.l, u.u), - checked::divide(t.u, u.l), - checked::divide(t.u, u.u) - } - : - std::initializer_list> { - checked::divide(t.l, u.l), - checked::divide(t.l, -1), - checked::divide(t.u, u.l), - checked::divide(t.u, -1), - checked::divide(t.l, 1), - checked::divide(t.l, u.u), - checked::divide(t.u, 1), - checked::divide(t.u, u.u) - } +// assert(u.l != 0 || u.u != 0); + return detail::minmax>( + (u.l == 0) + ? std::initializer_list> { + checked::divide(t.l, +1), + checked::divide(t.l, u.u), + checked::divide(t.u, +1), + checked::divide(t.u, u.u) + } + : (0 == u.u) + ? std::initializer_list> { + checked::divide(t.l, u.l), + checked::divide(t.l, -1), + checked::divide(t.u, u.l), + checked::divide(t.u, -1) + } + : (u.u < 0 || 0 < u.l) // divisor range excludes 0 + ? std::initializer_list> { + checked::divide(t.l, u.l), + checked::divide(t.l, u.u), + checked::divide(t.u, u.l), + checked::divide(t.u, u.u) + } + : std::initializer_list> { // divisor includes 0 + checked::divide(t.l, u.l), + checked::divide(t.l, -1), + checked::divide(t.l, +1), + checked::divide(t.l, u.u), + + checked::divide(t.u, u.l), + checked::divide(t.u, -1), + checked::divide(t.u, +1), + checked::divide(t.u, u.u) + } ); -} + } +// modulus of two intervals. This will give a new range of for the modulus. But +// it doesn't consider the possibility that the radix is zero. This will +// have to be considered separately. template -constexpr inline checked_result> divide( +constexpr interval> modulus( const interval & t, const interval & u ){ - // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - if(u.l <= 0 && u.u >= 0) - return checked_result>( - exception_type::domain_error, - "interval divisor includes zero" - ); - return divide_nz(t, u); + /* Turns out that getting the exact range is a suprisingly difficult + * problem. But we can get a reasonable guaranteed range with the following + * simple formula (due to Skona Brittain): + * + * [-m + 1, m - 1] where m is max(|c|, |d|). + * + * where the operation is [a,b] % [c,d] + * + * But a naive implementation of this algorithm doesn't work. We have a + * problem with (8 bit integers for illustration) abs(-128) -> -128! So we + * can't get the absolute value of the minimum value ! + * + * so we impement the above in a slightly different way. + */ + + // const R xa = (t.l > 0) ? t.l - 1 : t.l + 1; // a + // const R xb = (t.u > 0) ? t.u - 1 : t.u + 1; // b + const checked_result xc = (u.l > 0) ? u.l - 1 : (u.l < 0) ? -(u.l + 1) : 0; // c + const checked_result xd = (u.u > 0) ? u.u - 1 : (u.u < 0) ? -(u.u + 1) : 0; // d + + // special care to void problem when inverting -128 + const checked_result mxc = checked::subtract(0, xc); + const checked_result mxd = checked::subtract(0, xd); + + return (xc < xd) + ? interval>(mxd, xd) + : interval>(mxc, xc); + } template -constexpr checked_result> modulus_nz( +constexpr interval> left_shift( const interval & t, const interval & u ){ - // adapted from https://en.wikipedia.org/wiki/Modulo_operation - return detail::minmax( (u.l < 0 && 0 < u.u) ? - // if divisor range includes zero then we need include +1 and -1 - // among the possible divisors. But since % +1 and % -1 yield - // the same result, which is 0, then adding 0 to the possibilities - // is sufficient to make sure we've got the whole possible range - std::initializer_list> { - checked::modulus(t.l, u.l), - checked::modulus(t.l, u.u), - checked::modulus(t.u, u.l), - checked::modulus(t.u, u.u), - 0 // % 1 or % -1 - } - : - // if divisor range doesn't include zero, the following should be - // sufficient. - std::initializer_list> { - checked::modulus(t.l, u.l), - checked::modulus(t.l, u.u), - checked::modulus(t.u, u.l), - checked::modulus(t.u, u.u) - } - ); -} - -template -constexpr checked_result> modulus( - const interval & t, - const interval & u -){ - // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic - if(u.l <= 0 && u.u >= 0) - return checked_result>( - exception_type::domain_error, - "interval modulus includes zero" - ); - return modulus_nz(t, u); -} - -template -constexpr checked_result> left_shift( - const interval & t, - const interval & u -){ - return detail::minmax( std::initializer_list> { + return interval>{ checked::left_shift(t.l, u.l), - checked::left_shift(t.l, u.u), - checked::left_shift(t.u, u.l), - checked::left_shift(t.u, u.u) - }); + checked::left_shift(t.u, u.u), + }; } template -constexpr checked_result> right_shift( +constexpr interval> right_shift( const interval & t, const interval & u ){ - return detail::minmax( std::initializer_list> { - checked::right_shift(t.l, u.l), + return interval>{ checked::right_shift(t.l, u.u), - checked::right_shift(t.u, u.l), - checked::right_shift(t.u, u.u) - }); + checked::right_shift(t.u, u.l) + }; } template -constexpr checked_result> right_shift_positive( - const interval & t, - const interval & u -){ - const U ul = safe_compare::greater_than(0, u.l) ? 0 : u.l; - - const U ux = boost::numeric::log(std::numeric_limits::max()); - const U uu = safe_compare::less_than(u.u, ux) ? u.u : ux; - - return detail::minmax( std::initializer_list> { - checked::right_shift(t.l, ul), - checked::right_shift(t.l, uu), - checked::right_shift(t.u, ul), - checked::right_shift(t.u, uu) - }); -} - -template -constexpr checked_result> intersection( +constexpr interval> intersection( const interval & t, const interval & u ){ @@ -329,7 +313,7 @@ constexpr checked_result> intersection( } template -constexpr checked_result> union_interval( +constexpr interval> union_interval( const interval & t, const interval & u ){ @@ -428,7 +412,7 @@ operator<<( ){ return os << '[' << i.l << ',' << i.u << ']'; } -template +template inline std::basic_ostream & operator<<( std::basic_ostream & os, @@ -438,7 +422,7 @@ operator<<( return os; } -template +template inline std::basic_ostream & operator<<( std::basic_ostream & os, diff --git a/include/safe_base.hpp b/include/safe_base.hpp index e9b5897..d562ddb 100644 --- a/include/safe_base.hpp +++ b/include/safe_base.hpp @@ -155,17 +155,8 @@ class safe_base { std::basic_istream & is, safe_base & t ){ - int tx; - if(::std::is_same::value - || ::std::is_same::value - || ::std::is_same::value - ){ - is >> tx; - t = tx; - } - else{ - is >> t; - } + is >> t.m_t; + t.validated_cast(t.m_t); // no need to store result if(is.fail()) E::domain_error("error in file input"); return is; diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index daecba0..f6b0015 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -14,11 +14,14 @@ #include #include // is_base_of, is_same, enable_if +#include // max +#include #include #include #include #include // lazy_enable_if +#include #include "safe_base.hpp" #include "safe_literal.hpp" @@ -147,6 +150,7 @@ operator R () const { indeterminate(r_interval < this_interval), "safe type cannot be constructed with this type" ); + return boost::mpl::if_c< r_interval.includes(this_interval), typename validate_detail::exception_not_possible, @@ -249,10 +253,7 @@ template struct addition_result { using promotion_policy = typename common_promotion_policy::type; using result_base_type = - typename promotion_policy::template addition_result< - T, - U - >::type; + typename promotion_policy::template addition_result::type; constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), @@ -265,26 +266,24 @@ struct addition_result { }; // when we add the temporary intervals above, we'll get a new interval // with the correct range for the sum ! - constexpr static const checked_result> r_interval = + constexpr static const interval> r_interval = add(t_interval, u_interval); - constexpr static bool exception_possible() { - return r_interval.exception(); + constexpr static const bool exception_possible() { + return r_interval.l.exception() || r_interval.u.exception(); } - constexpr static const interval type_interval = - exception_possible() ? - interval{} - : - static_cast>(r_interval) - ; using exception_policy = typename common_exception_policy::type; struct safe_type { using type = safe_base< result_base_type, - type_interval.l, - type_interval.u, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u), promotion_policy, exception_policy >; @@ -368,45 +367,39 @@ constexpr inline operator+=(T & t, const U & u){ template struct subtraction_result { using promotion_policy = typename common_promotion_policy::type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; using result_base_type = - typename promotion_policy::template subtraction_result< - t_base_type, - u_base_type - >::type; + typename promotion_policy::template subtraction_result::type; - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; // when we add the temporary intervals above, we'll get a new interval // with the correct range for the difference ! - constexpr static const checked_result> r_interval - = subtract(t_interval, u_interval); + constexpr static const interval> r_interval = + subtract(t_interval, u_interval); - constexpr static bool exception_possible() { - return ! r_interval.no_exception(); + constexpr static const bool exception_possible() { + return r_interval.l.exception() || r_interval.u.exception(); } - constexpr static const interval type_interval = - exception_possible() ? - interval{} - : - static_cast>(r_interval) - ; using exception_policy = typename common_exception_policy::type; + struct safe_type { using type = safe_base< result_base_type, - type_interval.l, - type_interval.u, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u), promotion_policy, exception_policy >; @@ -490,49 +483,36 @@ constexpr inline operator-=(T & t, const U & u){ template struct multiplication_result { using promotion_policy = typename common_promotion_policy::type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; using result_base_type = - typename promotion_policy::template multiplication_result< - t_base_type, - u_base_type - >::type; + typename promotion_policy::template multiplication_result::type; using exception_policy = typename common_exception_policy::type; struct safe_type { - // filter out case were overflow cannot occur - // note: subtle trickery. Suppose t is safe_range. Then - // std::numeric_limits::min() will be safe_range t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - // when we add the temporary intervals above, we'll get a new interval - // with the correct range for the sum ! - constexpr static const checked_result> r_interval - = multiply(t_interval, u_interval); + constexpr static const interval> r_interval = + multiply(t_interval, u_interval); - constexpr static bool exception_possible() { - return ! r_interval.no_exception(); + constexpr static const bool exception_possible() { + return r_interval.l.exception() || r_interval.u.exception(); } - constexpr static const interval type_interval = - exception_possible() ? - interval{} - : - static_cast>(r_interval) - ; + using type = safe_base< result_base_type, - type_interval.l, - type_interval.u, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u), promotion_policy, exception_policy >; @@ -620,66 +600,55 @@ constexpr inline operator*=(T & t, const U & u){ ///////////////////////////////////////////////////////////////// // division +// key idea here - result will never be larger than T template struct division_result { using promotion_policy = typename common_promotion_policy::type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - using result_base_type = - typename promotion_policy::template division_result< - t_base_type, - u_base_type - >::type; + using r_base = typename promotion_policy::template division_result::type; + using t_base = typename base_type::type; + using u_base = typename base_type::type; using exception_policy = typename common_exception_policy::type; struct safe_type { - constexpr static const interval t_interval{ + constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> r_interval - = divide_nz(t_interval, u_interval); + constexpr static const interval> r_interval + = divide(t_interval, u_interval); - constexpr static bool exception_possible() { - // denominator better not be zero - static_assert( - (u_interval.l != 0 || u_interval.u != 0), - "cannot divide by zero" - ); + static_assert(! r_interval.l.exception(), "unexpected negative overflow"); + static_assert(! r_interval.u.exception(), "unexpected positive overflow"); + + constexpr static const bool exception_possible() { return - // if over/under flow or domain error possible - r_interval.exception() - // if the denominator can contain zero - || (u_interval.l <= 0 && u_interval.u >= 0 ) + u_interval.includes(0) + || + (u_interval.includes(-1) + && t_interval.includes(std::numeric_limits::min()) + ) ; } - - constexpr static const interval type_interval = - exception_possible() ? - interval{} - : - static_cast>(r_interval) - ; using type = safe_base< - result_base_type, - type_interval.l, - type_interval.u, + r_base, + static_cast(r_interval.l), + static_cast(r_interval.u), promotion_policy, exception_policy >; - constexpr static const type make(const result_base_type & t){ + constexpr static const type make(const r_base & t){ return type(t, std::false_type()); } }; struct unsafe_type { - using type = result_base_type; - constexpr static const type make(const result_base_type & t){ + using type = r_base; + constexpr static const type make(const r_base & t){ return t; } constexpr static bool exception_possible() { @@ -687,39 +656,52 @@ struct division_result { } }; using type_helper = typename boost::mpl::if_c< - std::numeric_limits::is_integer, + std::numeric_limits::is_integer, safe_type, unsafe_type >::type; using type = typename type_helper::type; - constexpr static const type make(const result_base_type & t){ + constexpr static const type make(const r_base & t){ return type_helper::make(t); } constexpr static bool exception_possible() { return type_helper::exception_possible(); } + constexpr static int bits = std::min( + std::numeric_limits::digits, + std::max(std::initializer_list{ + std::numeric_limits::digits, + std::numeric_limits::digits, + std::numeric_limits::digits + }) + (std::numeric_limits::is_signed ? 1 : 0) + ); + + using temp_base = typename boost::mpl::if_c< + std::numeric_limits::is_signed, + typename boost::int_t::least, + typename boost::uint_t::least + >::type; + // exception possible - constexpr static result_base_type + constexpr static r_base return_value(const T & t, const U & u, std::true_type){ - static_assert(exception_possible(), "implement runtime check"); - checked_result r = - promotion_policy::template divide( + checked_result r = checked::divide( base_value(t), base_value(u) ); + dispatch(r); - return static_cast(r); + return static_cast(r); } // exception not possible - constexpr static result_base_type + constexpr static r_base return_value(const T & t, const U & u, std::false_type){ - static_assert(! exception_possible(), "no runtime check"); return - static_cast(base_value(t)) - / static_cast(base_value(u)) + static_cast(base_value(t)) + / static_cast(base_value(u)) ; } }; @@ -760,50 +742,40 @@ constexpr inline operator/=(T & t, const U & u){ template struct modulus_result { using promotion_policy = typename common_promotion_policy::type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - using result_base_type = - typename promotion_policy::template modulus_result< - t_base_type, - u_base_type - >::type; - + using result_base_type = typename promotion_policy::template modulus_result::type; using exception_policy = typename common_exception_policy::type; struct safe_type { - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> r_interval - = modulus_nz(t_interval, u_interval); + constexpr static const interval> r_interval + = modulus(t_interval, u_interval); constexpr static bool exception_possible() { return // if over/under flow or domain error possible - r_interval.exception() + (r_interval.l.exception() || r_interval.u.exception()) // if the denominator can contain zero - || (u_interval.l <= 0 && u_interval.u >=0 ) + || u_interval.includes(0) ; } - constexpr static const interval type_interval = - exception_possible() ? - interval{} - : - static_cast>(r_interval) - ; - using type = safe_base< result_base_type, - type_interval.l, - type_interval.u, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u), promotion_policy, exception_policy >; @@ -864,7 +836,8 @@ typename std::enable_if< , typename modulus_result::type >::type -inline operator%(const T & t, const U & u){ +constexpr operator%(const T & t, const U & u){ + // see https://en.wikipedia.org/wiki/Modulo_operation using mr = modulus_result; return mr::make( mr::return_value( @@ -898,21 +871,22 @@ typename std::enable_if< bool >::type constexpr operator<(const T & lhs, const U & rhs) { - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - // filter out case were overflow cannot occur - constexpr const interval t_interval( + constexpr const interval::type> t_interval( base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) ); - constexpr const interval u_interval( + constexpr const interval::type> u_interval( base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) ); - return safe_compare::less_than(base_value(lhs), base_value(rhs)); ; + const boost::tribool x = t_interval < u_interval; + return + x ? true : + !x ? false: + safe_compare::less_than(base_value(lhs), base_value(rhs)); } template @@ -956,15 +930,12 @@ typename std::enable_if< bool >::type constexpr operator==(const T & lhs, const U & rhs) { - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; - - constexpr const interval t_interval( + constexpr const interval::type> t_interval( base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) ); - constexpr const interval u_interval( + constexpr const interval::type> u_interval( base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) ); @@ -998,44 +969,42 @@ constexpr operator!=(const T & lhs, const U & rhs) { template struct left_shift_result { using promotion_policy = typename common_promotion_policy::type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; using result_base_type = - typename promotion_policy::template left_shift_result< - t_base_type, - u_base_type - >::type; + typename promotion_policy::template left_shift_result::type; - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> r_interval { + static_assert(t_interval.u >= 0, "negative values can't be shifted"); + static_assert(u_interval.u >= 0, "shift count can't be negative"); + static_assert(u_interval.l <= std::numeric_limits::digits, + "shift count can't exceed argument size" + ); + + constexpr static const interval> r_interval { left_shift(t_interval, u_interval) }; - constexpr static bool exception_possible() { - return ! r_interval.no_exception(); + constexpr static const bool exception_possible() { + return r_interval.l.exception() || r_interval.u.exception(); } - constexpr static const interval type_interval = - exception_possible() ? - interval{} - : - static_cast>(r_interval) - ; - using exception_policy = typename common_exception_policy::type; using type = safe_base< result_base_type, - type_interval.l, - type_interval.u, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u), promotion_policy, exception_policy >; @@ -1110,44 +1079,42 @@ constexpr inline operator<<=(T & t, const U & u){ template struct right_shift_result { using promotion_policy = typename common_promotion_policy::type; - using t_base_type = typename base_type::type; - using u_base_type = typename base_type::type; using result_base_type = - typename promotion_policy::template right_shift_result< - t_base_type, - u_base_type - >::type; + typename promotion_policy::template right_shift_result::type; - constexpr static const interval t_interval{ + constexpr static const interval::type> t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const interval u_interval{ + constexpr static const interval::type> u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; - constexpr static const checked_result> r_interval{ + static_assert(t_interval.u >= 0, "negative values can't be shifted"); + static_assert(u_interval.l <= 0, "shift count can't be negative"); + static_assert(u_interval.l <= std::numeric_limits::digits, + "shift count can't exceed argument size" + ); + + constexpr static const interval> r_interval{ right_shift(t_interval, u_interval) }; - constexpr static bool exception_possible(){ - return ! r_interval.no_exception(); + constexpr static const bool exception_possible() { + return r_interval.l.exception() || r_interval.u.exception(); } - constexpr static const interval type_interval = - exception_possible() ? - interval{} - : - static_cast>(r_interval) - ; - using exception_policy = typename common_exception_policy::type; using type = safe_base< result_base_type, - type_interval.l, - type_interval.u, + r_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_interval.l), + r_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_interval.u), promotion_policy, exception_policy >; diff --git a/include/safe_literal.hpp b/include/safe_literal.hpp index 4a03ee0..c3a715a 100644 --- a/include/safe_literal.hpp +++ b/include/safe_literal.hpp @@ -150,7 +150,7 @@ using safe_unsigned_literal = safe_literal_impl< } // boost ///////////////////////////////////////////////////////////////// -// numeric limits for safe etc. +// numeric limits for safe_literal etc. #include @@ -167,6 +167,9 @@ class numeric_limits > { using SL = boost::numeric::safe_literal_impl; public: + constexpr static SL lowest() noexcept { + return SL(); + } constexpr static SL min() noexcept { return SL(); } diff --git a/include/utility.hpp b/include/utility.hpp index 4f8a303..84dca2c 100644 --- a/include/utility.hpp +++ b/include/utility.hpp @@ -18,40 +18,6 @@ namespace boost { namespace numeric { - // section 4.13 integer conversion rank - template - struct rank; - - template<> - struct rank : public std::integral_constant{}; - template<> - struct rank : public std::integral_constant{}; - template<> - struct rank : public std::integral_constant{}; - - template<> - struct rank : public std::integral_constant{}; - - template<> - struct rank : public std::integral_constant{}; - template<> - struct rank : public std::integral_constant{}; - - template<> - struct rank : public std::integral_constant{}; - template<> - struct rank : public std::integral_constant{}; - - template<> - struct rank : public std::integral_constant{}; - template<> - struct rank : public std::integral_constant{}; - - template<> - struct rank : public std::integral_constant{}; - template<> - struct rank : public std::integral_constant{}; - // the number of bits required to render the value in x template typename std::enable_if< @@ -97,6 +63,14 @@ namespace numeric { std::max({log(Min), log(Max)}) >::least ; + template + using bits = std::integral_constant< + int, + std::numeric_limits::digits + + std::numeric_limits::is_signed ? 1 : 0 + >; + + // used for debugging // usage - print_type; // provokes error message with name of type T @@ -107,8 +81,8 @@ namespace numeric { template struct print_value { - enum test : unsigned char { - value = N + 256 + enum test : char { + value = N < 0 ? N - 256 : N + 256 }; }; diff --git a/test/test_checked_divide.cpp b/test/test_checked_divide.cpp new file mode 100644 index 0000000..890c98b --- /dev/null +++ b/test/test_checked_divide.cpp @@ -0,0 +1,89 @@ +// 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 // EXIT_SUCCESS + +#include "test_checked_divide.hpp" +#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 + std::cout << (rval ? "success!" : "failure") << std::endl; + return ! rval ; +} + + diff --git a/test/test_checked_divide_automatic.cpp b/test/test_checked_divide_automatic.cpp deleted file mode 100644 index 46c4764..0000000 --- a/test/test_checked_divide_automatic.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// 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 // int_t::fast - -#include "../include/automatic.hpp" // bits -#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; - - using result_type = typename boost::mpl::if_c< - std::numeric_limits::is_signed - || std::numeric_limits::is_signed, - std::intmax_t, - std::uintmax_t - >::type; - - checked_result result - = checked::divide_automatic(v1, v2); - - if(result.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_automatic(v1, v2); - return false; - } - else - if(result.exception() - && expected_result != 'x'){ - std::cout - << "erroneously detected error " - << std::hex << result << av1 << " / " << av2 << std::dec << std::endl; - result = checked::divide_automatic(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*/ "................................", -/* 3*/ "................................", -/* 4*/ "................................", -/* 5*/ "................................", -/* 6*/ "................................", -/* 7*/ "................................", - -/* 8*/ "................................", -/* 9*/ "................................", -/*10*/ "................................", -/*11*/ "................................", -/*12*/ "................................", -/*13*/ "................................", -/*14*/ "...x...x...x...x................", -/*15*/ "................................", - -// 0 0 0 0 -// 01234567012345670123456701234567 -// 01234567890123456789012345678901 -/*16*/ "................................", -/*17*/ "................................", -/*18*/ "................................", -/*19*/ "................................", -/*20*/ "................................", -/*21*/ "................................", -/*22*/ "................................", -/*23*/ "................................", - -/*24*/ "................................", -/*25*/ "................................", -/*26*/ "................................", -/*27*/ "................................", -/*28*/ "................................", -/*29*/ "................................", -/*30*/ "xxxxxxxxxxxxxxxx................", -/*31*/ "xxxxxxxxxxxxxxxx................" -}; - -#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 - std::cout << (rval ? "success!" : "failure") << std::endl; - return ! rval ; -} - - diff --git a/test/test_checked_result.cpp b/test/test_checked_result.cpp index 352d408..9c2cf2b 100644 --- a/test/test_checked_result.cpp +++ b/test/test_checked_result.cpp @@ -22,7 +22,7 @@ bool test2(){ using namespace boost::numeric; checked_result x1(0); assert(0 == x1); - checked_result x2(exception_type::overflow_error, "exception message"); + checked_result x2(exception_type::positive_overflow_error, "exception message"); assert(! x2.no_exception()); return true; } diff --git a/test/test_cpp.cpp b/test/test_cpp.cpp index e6ba0a3..864ab0f 100644 --- a/test/test_cpp.cpp +++ b/test/test_cpp.cpp @@ -5,6 +5,7 @@ using namespace boost::numeric; // create custom policy which emulates native one +// so we can compare the results using custom = cpp< CHAR_BIT, CHAR_BIT * sizeof(short), @@ -15,7 +16,7 @@ using custom = cpp< template using custom_result_type = - typename custom::usual_arithmetic_conversions< + custom::usual_arithmetic_conversions< custom::integral_promotion, custom::integral_promotion >; @@ -35,13 +36,43 @@ void test_integral_promotion(T){ \ ); \ } +static_assert( + custom::rank::value == 1, + "rank char != 1" +); +static_assert( + custom::rank::type>::value == 1, + "rank signed char != 1" +); +static_assert( + custom::rank::type>::value == 1, + "rank unsigned char != 1" +); +static_assert( + custom::rank::value == 2, + "rank short != 2" +); +static_assert( + custom::rank::type>::value == 2, + "rank unsigned short != 2" +); +static_assert( + custom::rank::value == 3, + "rank int != 3" +); +static_assert( + custom::rank::type>::value == 3, + "rank unsigned int != 3" +); + test_integral_promotion(char) test_integral_promotion(signed char) test_integral_promotion(unsigned char) test_integral_promotion(signed short) test_integral_promotion(unsigned short) - +// compare our custom promotion policy with the native +// one. They should be the same. #define test_usual_arithmetic_conversions(T1, T2) \ void test_usual_arithmetic_conversions(T1, T2){ \ static_assert( \ diff --git a/test/test_divide.hpp b/test/test_divide.hpp index 8ed197b..bc13a3c 100644 --- a/test/test_divide.hpp +++ b/test/test_divide.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include "../include/safe_integer.hpp" @@ -21,36 +21,30 @@ bool test_divide( const char *av2, char expected_result ){ - using namespace boost::numeric; - // can't so this as -min / -1 invokes hardware trap - - decltype(v1 / v2) unsafe_result; - if(expected_result == '.') - //unsafe_result = v1 / v2; + std::cout << "testing"<< std::endl; { - 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; - + std::cout << "safe<" << av1 << "> / " << av2 << " -> "; static_assert( - is_safe::value, + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); + static_assert( + boost::numeric::is_safe::value, "Expression failed to return safe type" ); - + result_type result; 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' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 / v2; @@ -65,10 +59,9 @@ bool test_divide( << 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' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 / v2; @@ -81,7 +74,10 @@ bool test_divide( } { std::cout << "testing " << av1 << " / " << "safe<"<< av2 << "> -> "; - static_assert(is_safe >::value, "safe_t not safe!"); + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); safe_t t2 = v2; @@ -89,7 +85,7 @@ bool test_divide( result_type result; static_assert( - is_safe::value, + boost::numeric::is_safe::value, "Expression failed to return safe type" ); @@ -99,10 +95,9 @@ bool test_divide( << 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' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ v1 / t2; @@ -117,16 +112,15 @@ bool test_divide( << 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' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ v1 / t2; } catch(std::exception){} - assert(result == unsafe_result); + // assert(result == unsafe_result); return false; } } @@ -140,7 +134,7 @@ bool test_divide( result_type result; static_assert( - is_safe::value, + boost::numeric::is_safe::value, "Expression failed to return safe type" ); @@ -150,10 +144,9 @@ bool test_divide( << 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' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 / t2; @@ -168,16 +161,15 @@ bool test_divide( << 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' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 / t2; } catch(std::exception){} - assert(result == unsafe_result); + // assert(result == unsafe_result); return false; } } diff --git a/test/test_divide_automatic.cpp b/test/test_divide_automatic.cpp index 781b3ec..a6a029b 100644 --- a/test/test_divide_automatic.cpp +++ b/test/test_divide_automatic.cpp @@ -6,7 +6,6 @@ #include #include -//#include #include "../include/safe_integer.hpp" #include "../include/automatic.hpp" @@ -29,23 +28,23 @@ const char *test_division_result[VALUE_ARRAY_SIZE] = { // 0 0 0 0 // 01234567012345670123456701234567 // 01234567890123456789012345678901 -/* 0*/ "................................", -/* 1*/ "................................", -/* 2*/ "................................", -/* 3*/ "................................", -/* 4*/ "................................", -/* 5*/ "................................", -/* 6*/ "................................", -/* 7*/ "................................", +/* 0*/ "..............................xx", +/* 1*/ "..............................xx", +/* 2*/ "..............................xx", +/* 3*/ "..............................xx", +/* 4*/ "..............................xx", +/* 5*/ "..............................xx", +/* 6*/ "..............................xx", +/* 7*/ "..............................xx", -/* 8*/ "................................", -/* 9*/ "................................", -/*10*/ "................................", -/*11*/ "................................", -/*12*/ "................................", -/*13*/ "................................", -/*14*/ "...x...x...x...x................", -/*15*/ "................................", +/* 8*/ "..............................xx", +/* 9*/ "..............................xx.", +/*10*/ "..............................xx", +/*11*/ "..............................xx", +/*12*/ "..............................xx", +/*13*/ "..............................xx", +/*14*/ "...x...x...x...x..............xx", +/*15*/ "..............................xx", // 0 0 0 0 // 01234567012345670123456701234567 diff --git a/test/test_divide_native.cpp b/test/test_divide_native.cpp index 3772300..986c495 100644 --- a/test/test_divide_native.cpp +++ b/test/test_divide_native.cpp @@ -6,7 +6,6 @@ #include #include -//#include #include "../include/safe_integer.hpp" diff --git a/test/test_interval.cpp b/test/test_interval.cpp index 8de3704..1daa421 100644 --- a/test/test_interval.cpp +++ b/test/test_interval.cpp @@ -2,14 +2,16 @@ #include #include #include -#include // demangle #include // max, min +#include +#include #include #include "../include/checked_result.hpp" #include "../include/interval.hpp" +// test simple interval addition bool test1(){ using namespace boost::numeric; std::cout << "test1" << std::endl; @@ -18,11 +20,14 @@ bool test1(){ interval y(-128, 126); std::cout << "y = " << y << std::endl; assert(static_cast>(add(x,x)) == y); + if(add(x,x) != y) + return false; std::cout << "x + x =" << add(x, x) << std::endl; std::cout << "x - x = " << subtract(x, x) << std::endl; return true; } +// test simple interval equality bool test2(){ using namespace boost::numeric; std::cout << "test2" << std::endl; @@ -32,6 +37,7 @@ bool test2(){ return true; } +// test erroneous addition bool test3(){ using namespace boost::numeric; std::cout << "test3" << std::endl; @@ -40,11 +46,29 @@ bool test3(){ interval u; std::cout << "u = " << u << std::endl; using max_t = unsigned long long; - checked_result> r = add(t, u); + interval> r = add(t, u); std::cout << "r = " << r << std::endl; return true; } +// test simple interval addition +bool test6(){ + using namespace boost::numeric; + std::cout << "test6" << std::endl; + interval x; + std::cout << "x = " << x << std::endl; + interval y(8, 8); + std::cout << "y = " << y << std::endl; + + assert(static_cast>(add(x,x)) == y); + if(add(x,x) != y) + return false; + std::cout << "x + x =" << add(x, x) << std::endl; + std::cout << "x - x = " << subtract(x, x) << std::endl; + return true; +} + +// test simple interval inclusion template bool test5(){ using namespace boost::numeric; @@ -62,23 +86,13 @@ bool test5(){ return ExpectedResult == t.includes(u); } -#include - namespace test4 { using namespace boost::numeric; using max_t = std::intmax_t; - template - constexpr static const interval r_upper(const interval & t){ - static_assert( - std::is_literal_type< interval >::value, - "interval is not literal type" - ); - return interval( - std::max(Tx(1), t.l), - t.u - ); - } + // utilities + + // figure the lower portion of range which includes zero template constexpr static const interval r_lower(const interval & t){ static_assert( @@ -91,116 +105,52 @@ namespace test4 { ); } - template - constexpr static const checked_result> r( - const interval & t, - const interval & u - ){ - if(std::numeric_limits::is_signed){ - if(u.l > 0 || u.u < 0){ - return divide(t, u); - } - else{ - checked_result> lower = divide(t,r_lower(u)); - if(! lower.no_exception()) - return lower; - checked_result> upper = divide(t,r_upper(u)); - if(! upper.no_exception()) - return upper; - const interval< max_t> & il = lower; - const interval< max_t> & iu = upper; - return - interval< max_t>( - std::min(il.l, iu.l), - std::max(il.u, iu.u) - ); - } - } - else{ // u is unsigned - if(u.l > 0) - return divide(t, u); - else - return divide(t, r_upper(u)) ; - }; - }; + // figure the upper portion of range which includes zero + template + constexpr static const interval r_upper(const interval & t){ + static_assert( + std::is_literal_type< interval >::value, + "interval is not literal type" + ); + return interval( + std::max(Tx(1), t.l), + t.u + ); + } - bool test1(){ + template + bool divide_result(){ using namespace boost::numeric; - int status; - std::cout << "test4::test1 int8_t / int8_t new range ignoring zero" << std::endl; - const interval t; + std::cout << "divide result" << std::endl; + const interval t; std::cout - << abi::__cxa_demangle(typeid(t).name(),0,0,&status) + << boost::core::demangle(typeid(t).name()) << " t = " << t << std::endl; - const interval u; + const interval u; std::cout - << abi::__cxa_demangle(typeid(u).name(),0,0,&status) + << boost::core::demangle(typeid(u).name()) << " u = " << u << std::endl; - const interval rx = r(t, u); + const interval> rx = divide(t, u); // r(t, u); std::cout - << abi::__cxa_demangle(typeid(rx).name(),0,0,&status) - << " rx = " - << rx - << std::endl; - return true; - } - bool test2(){ - using namespace boost::numeric; - int status; - std::cout << "test4::test2 int8_t / int64_t new range ignoring zero" << std::endl; - const interval t; - std::cout - << abi::__cxa_demangle(typeid(t).name(),0,0,&status) - << " t = " - << t << std::endl; - const interval u; - std::cout - << abi::__cxa_demangle(typeid(u).name(),0,0,&status) - << " u = " - << u << std::endl; - const interval rx = r(t, u); - std::cout - << abi::__cxa_demangle(typeid(rx).name(),0,0,&status) - << " rx = " - << rx - << std::endl; - return true; - } - bool test3(){ - using namespace boost::numeric; - int status; - std::cout << "test4::test3 int8_t / int64_t new range ignoring zer0" << std::endl; - const interval t; - std::cout - << abi::__cxa_demangle(typeid(t).name(),0,0,&status) - << " t = " - << t << std::endl; - const interval u; - std::cout - << abi::__cxa_demangle(typeid(u).name(),0,0,&status) - << " u = " - << u << std::endl; - const checked_result> rx = r(t, u); - std::cout - << abi::__cxa_demangle(typeid(rx).name(),0,0,&status) + << boost::core::demangle(typeid(rx).name()) << " rx = " << rx << std::endl; return true; } + template bool test4(){ using namespace boost::numeric; - int status; - std::cout << "test4::test4 multiply" << std::endl; + std::cout << "test4::multiply" << std::endl; const interval t_interval = { base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; std::cout - << abi::__cxa_demangle(typeid(t_interval).name(),0,0,&status) + << boost::core::demangle(typeid(t_interval).name()) << " t_interval = " << t_interval << std::endl; @@ -209,15 +159,15 @@ namespace test4 { base_value(std::numeric_limits::max()) }; std::cout - << abi::__cxa_demangle(typeid(u_interval).name(),0,0,&status) + << boost::core::demangle(typeid(u_interval).name()) << " u_interval = " << u_interval << std::endl; using R = decltype(U() * T()); - const checked_result > r_interval + const interval> r_interval = multiply(t_interval, u_interval); std::cout - << abi::__cxa_demangle(typeid(r_interval).name(),0,0,&status) + << boost::core::demangle(typeid(r_interval).name()) << " r_interval = " << r_interval << std::endl; @@ -226,14 +176,13 @@ namespace test4 { template bool test5(){ using namespace boost::numeric; - int status; - std::cout << "test4::test4 divide_nz" << std::endl; + std::cout << "test4::test4 divide" << std::endl; const interval t_interval = { base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; std::cout - << abi::__cxa_demangle(typeid(t_interval).name(),0,0,&status) + << boost::core::demangle(typeid(t_interval).name()) << " t_interval = " << t_interval << std::endl; @@ -242,15 +191,15 @@ namespace test4 { base_value(std::numeric_limits::max()) }; std::cout - << abi::__cxa_demangle(typeid(u_interval).name(),0,0,&status) + << boost::core::demangle(typeid(u_interval).name()) << " u_interval = " << u_interval << std::endl; using R = decltype(U() * T()); - const checked_result > r_interval - = divide_nz(t_interval, u_interval); + const interval> r_interval + = divide(t_interval, u_interval); std::cout - << abi::__cxa_demangle(typeid(r_interval).name(),0,0,&status) + << boost::core::demangle(typeid(r_interval).name()) << " r_interval = " << r_interval << std::endl; @@ -259,14 +208,13 @@ namespace test4 { template bool test6(){ using namespace boost::numeric; - int status; std::cout << "test4::test4 subtract" << std::endl; const interval t_interval = { base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; std::cout - << abi::__cxa_demangle(typeid(t_interval).name(),0,0,&status) + << boost::core::demangle(typeid(t_interval).name()) << " t_interval = " << t_interval << std::endl; @@ -275,15 +223,42 @@ namespace test4 { base_value(std::numeric_limits::max()) }; std::cout - << abi::__cxa_demangle(typeid(u_interval).name(),0,0,&status) + << boost::core::demangle(typeid(u_interval).name()) << " u_interval = " << u_interval << std::endl; using R = decltype(U() * T()); - const checked_result > r_interval + const interval> r_interval = subtract(t_interval, u_interval); std::cout - << abi::__cxa_demangle(typeid(r_interval).name(),0,0,&status) + << boost::core::demangle(typeid(r_interval).name()) + << " r_interval = " + << r_interval + << std::endl; + return true; + } + template + bool test7( + const interval & t_interval, + const interval & u_interval + ){ + using namespace boost::numeric; + std::cout << "test4::test4 left shift" << std::endl; + std::cout + << boost::core::demangle(typeid(t_interval).name()) + << " t_interval = " + << t_interval + << std::endl; + std::cout + << boost::core::demangle(typeid(u_interval).name()) + << " u_interval = " + << u_interval + << std::endl; + using R = decltype(T() << U()); + const interval> r_interval + = left_shift(t_interval, u_interval); + std::cout + << boost::core::demangle(typeid(r_interval).name()) << " r_interval = " << r_interval << std::endl; @@ -291,53 +266,100 @@ namespace test4 { } template bool test7(){ + return test7(interval(), interval()); + } + template + bool test8( + const interval & t_interval, + const interval & u_interval + ){ using namespace boost::numeric; - int status; - std::cout << "test4::test4 left shift" << std::endl; - const interval t_interval = { - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; + std::cout << "test4::test4 right shift" << std::endl; std::cout - << abi::__cxa_demangle(typeid(t_interval).name(),0,0,&status) + << boost::core::demangle(typeid(t_interval).name()) << " t_interval = " << t_interval << std::endl; - const interval u_interval = { - base_value(std::numeric_limits::min()), - base_value(std::numeric_limits::max()) - }; std::cout - << abi::__cxa_demangle(typeid(u_interval).name(),0,0,&status) + << boost::core::demangle(typeid(u_interval).name()) << " u_interval = " << u_interval << std::endl; - using R = T; - const checked_result > r_interval - = left_shift(t_interval, u_interval); + using R = decltype(T() << U()); + const interval> r_interval + = right_shift(t_interval, u_interval); std::cout - << abi::__cxa_demangle(typeid(r_interval).name(),0,0,&status) + << boost::core::demangle(typeid(r_interval).name()) << " r_interval = " << r_interval << std::endl; return true; } template + bool test8(){ + return test8(interval(), interval()); + } + template + bool test9( + const interval & t_interval, + const interval & u_interval + ){ + using namespace boost::numeric; + std::cout << "test4::test4 mod" << std::endl; + std::cout + << boost::core::demangle(typeid(t_interval).name()) + << " t_interval = " + << t_interval + << std::endl; + std::cout + << boost::core::demangle(typeid(u_interval).name()) + << " u_interval = " + << u_interval + << std::endl; + using R = decltype(T() % U()); + const interval> r_interval + = modulus(t_interval, u_interval); + std::cout + << boost::core::demangle(typeid(r_interval).name()) + << " r_interval = " + << r_interval + << std::endl; + return true; + } + template + bool test9(){ + return test9(interval(), interval()); + } + template bool test(){ return test4() && test5() && test6() && - test7() + test7() && + test8() && + test9() ; } } // test4 int main(){ + using namespace boost::numeric; bool rval = ( test1() && test2() && test3() && + + test4::test8() && + test4::test9() && + test4::test7() && + test4::test7() && + test4::test7( + interval(), + interval(8, 8) + ) && + test4::test8() && + test4::test8() && test5() && test5() && test5() && @@ -346,9 +368,17 @@ int main(){ test5() && test5() && test5() && - test4::test1() && - test4::test2() && - test4::test3() && + + test4::divide_result() && + test4::divide_result() && + test4::divide_result() && + test4::divide_result() && + test4::divide_result() && + test4::divide_result() && + test4::divide_result() && + test4::divide_result() && + + test4::test() && test4::test() && test4::test() && test4::test() && diff --git a/test/test_modulus.hpp b/test/test_modulus.hpp index 7e10b16..5f8f631 100644 --- a/test/test_modulus.hpp +++ b/test/test_modulus.hpp @@ -25,7 +25,7 @@ bool test_modulus( { safe_t t1 = v1; using result_type = decltype(t1 % v2); - std::cout << "safe<" << av1 << "> & " << av2 << " -> "; + std::cout << "safe<" << av1 << "> % " << av2 << " -> "; static_assert( boost::numeric::is_safe >::value, "safe_t not safe!" diff --git a/test/test_modulus_automatic.cpp b/test/test_modulus_automatic.cpp index 88b4d2a..5b58c31 100644 --- a/test/test_modulus_automatic.cpp +++ b/test/test_modulus_automatic.cpp @@ -32,7 +32,7 @@ const char *test_modulus_result[VALUE_ARRAY_SIZE] = { /* 1*/ "................................", /* 2*/ "................................", /* 3*/ "................................", -/* 4*/ ".................................", +/* 4*/ "................................", /* 5*/ "................................", /* 6*/ "................................", /* 7*/ "................................", diff --git a/test/test_multiply.hpp b/test/test_multiply.hpp index b011ec6..16803d9 100644 --- a/test/test_multiply.hpp +++ b/test/test_multiply.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include "../include/safe_integer.hpp" @@ -21,14 +21,16 @@ bool test_multiply( const char *av2, char expected_result ){ - std::cout - << "testing " - << av1 << " * " << av2 - << std::endl; + std::cout << "testing"<< std::endl; { safe_t t1 = v1; using result_type = decltype(t1 * v2); + std::cout << "safe<" << av1 << "> * " << av2 << " -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" @@ -37,12 +39,13 @@ bool test_multiply( 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 multiplication " - << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 * v2; @@ -52,12 +55,13 @@ bool test_multiply( } } 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 multiplication " - << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 * v2; @@ -70,6 +74,11 @@ bool test_multiply( { safe_t t2 = v2; using result_type = decltype(v1 * t2); + std::cout << av1 << " * " << "safe<" << av2 << "> -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" @@ -78,12 +87,13 @@ bool test_multiply( 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 multiplication " - << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ v1 * t2; @@ -93,12 +103,13 @@ bool test_multiply( } } 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 multiplication " - << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ v1 * t2; @@ -113,6 +124,7 @@ bool test_multiply( safe_t t2 = v2; using result_type = decltype(t1 * t2); + std::cout << "safe<" << av1 << "> * " << "safe<" << av2 << "> -> "; static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" @@ -121,12 +133,13 @@ bool test_multiply( 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 multiplication " - << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 * t2; @@ -136,12 +149,13 @@ bool test_multiply( } } 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 multiplication " - << abi::__cxa_demangle(ti.name(),0,0,&status) << '\n' + << boost::core::demangle(ti.name()) << '\n' << std::endl; try{ t1 * t2; diff --git a/test/test_right_shift_automatic.cpp b/test/test_right_shift_automatic.cpp index 5e38975..5cbe07f 100644 --- a/test/test_right_shift_automatic.cpp +++ b/test/test_right_shift_automatic.cpp @@ -49,23 +49,23 @@ const char *test_right_shift_result[VALUE_ARRAY_SIZE] = { // 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*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*17*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*18*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*19*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*20*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*21*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*22*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*23*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", -/*24*/ "..xx..xx..xx..xx................", -/*25*/ "..xx..xx..xx..xx................", -/*26*/ "..xx..xx..xx..xx................", -/*27*/ "..xx..xx..xx..xx................", -/*28*/ "..xx..xx..xx..xx................", -/*29*/ "..xx..xx..xx..xx................", -/*30*/ "..xx..xx..xx..xx................", -/*31*/ "..xx..xx..xx..xx................" +/*24*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*25*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*26*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*27*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*28*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*29*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*30*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*31*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx" }; #include diff --git a/test/test_right_shift_native.cpp b/test/test_right_shift_native.cpp index 8ebbcce..acd898d 100644 --- a/test/test_right_shift_native.cpp +++ b/test/test_right_shift_native.cpp @@ -49,23 +49,23 @@ const char *test_right_shift_result[VALUE_ARRAY_SIZE] = { // 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*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*17*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*18*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*19*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*20*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*21*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*22*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*23*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", -/*24*/ "..xx..xx..xx..xx................", -/*25*/ "..xx..xx..xx..xx................", -/*26*/ "..xx..xx..xx..xx................", -/*27*/ "..xx..xx..xx..xx................", -/*28*/ "..xx..xx..xx..xx................", -/*29*/ "..xx..xx..xx..xx................", -/*30*/ "..xx..xx..xx..xx................", -/*31*/ "..xx..xx..xx..xx................" +/*24*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*25*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*26*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*27*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*28*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*29*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*30*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*31*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx." }; #include diff --git a/test/test_subtract.hpp b/test/test_subtract.hpp index 3b5a22a..f3c8c54 100644 --- a/test/test_subtract.hpp +++ b/test/test_subtract.hpp @@ -9,7 +9,6 @@ #include #include -//#include #include "../include/safe_integer.hpp" @@ -21,17 +20,25 @@ bool test_subtract( const char *av2, char expected_result ){ - std::cout - << "testing " - << av1 << " - " << av2 - << std::endl; + std::cout << "testing"<< std::endl; { safe_t t1 = v1; using result_type = decltype(t1 - v2); + std::cout << "safe<" << av1 << "> - " << av2 << " -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); result_type result; try{ result = t1 - v2; + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; static_assert( boost::numeric::is_safe::value, "Expression failed to return safe type" @@ -50,6 +57,8 @@ bool test_subtract( } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ std::cout << "erroneously detected error in subtraction " @@ -67,14 +76,21 @@ bool test_subtract( { safe_t t2 = v2; using result_type = decltype(v1 - t2); + std::cout << av1 << " - " << "safe<" << av2 << "> -> "; + static_assert( + boost::numeric::is_safe >::value, + "safe_t not safe!" + ); + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); result_type result; 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'){ std::cout << "failed to detect error in subtraction " @@ -89,6 +105,8 @@ bool test_subtract( } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ std::cout << "erroneously detected error in subtraction " @@ -107,12 +125,19 @@ bool test_subtract( safe_t t1 = v1; safe_t t2 = v2; using result_type = decltype(t1 - t2); + std::cout << "safe<" << av1 << "> - " << "safe<" << av2 << "> -> "; + static_assert( + boost::numeric::is_safe::value, + "Expression failed to return safe type" + ); result_type result; try{ result = t1 - t2; + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; static_assert( - boost::numeric::is_safe::value, + boost::numeric::is_safe::value, "Expression failed to return safe type" ); if(expected_result == 'x'){ @@ -129,6 +154,8 @@ bool test_subtract( } } catch(std::exception){ + std::cout << std::hex << result << "(" << std::dec << result << ")" + << std::endl; if(expected_result == '.'){ std::cout << "erroneously detected error in subtraction " diff --git a/test/test_z.cpp b/test/test_z.cpp index 7586c70..ef3f0df 100644 --- a/test/test_z.cpp +++ b/test/test_z.cpp @@ -149,23 +149,6 @@ int main(){ #include "../include/cpp.hpp" #include "../include/safe_common.hpp" -using pic16_promotion = boost::numeric::cpp< - 8, // char - 8, // short - 8, // int - 16, // long - 32 // long long ->; - -using pr = pic16_promotion::rank; - -int main(){ - return 0; -} - -#include "../include/cpp.hpp" -#include "../include/safe_common.hpp" - using namespace boost::numeric; // create custom policy which emulates native one @@ -491,8 +474,37 @@ void f4(){ constexpr const safe l2 = j + k; } -#endif +#include "../include/interval.hpp" int main(){ return 0; } + +#endif + +#include "../include/utility.hpp" +#include "../include/cpp.hpp" +#include "../include/safe_common.hpp" + +using pic16_promotion = boost::numeric::cpp< + 8, // char + 8, // short + 8, // int + 16, // long + 32 // long long +>; + +/* + template + using result_type = usual_arithmetic_conversions< + integral_promotion::type>, + integral_promotion::type> + >; +*/ + +using pr = pic16_promotion::rank; + +int main(){ + return 0; +} +