From 5715f5126d5b9e45c8c49dcd153f606523145403 Mon Sep 17 00:00:00 2001 From: Robert Ramey Date: Mon, 6 Mar 2017 09:19:14 -0800 Subject: [PATCH] miscelleanous corrections --- doc/boostbook/tutorial.xml | 14 +- examples/18F252_desktop.h | 7 +- examples/example1.cpp | 2 +- examples/example7.cpp | 98 ++++------- include/automatic.hpp | 102 +++++++++++- include/checked.hpp | 242 +++++++++++++++------------- include/safe_base.hpp | 7 +- include/safe_base_operations.hpp | 61 ++++++- include/safe_literal.hpp | 32 +++- include/utility.hpp | 14 +- test/test_left_shift_automatic.cpp | 6 +- test/test_left_shift_native.cpp | 28 ++-- test/test_right_shift_automatic.cpp | 32 ++-- test/test_right_shift_native.cpp | 32 ++-- 14 files changed, 418 insertions(+), 259 deletions(-) diff --git a/doc/boostbook/tutorial.xml b/doc/boostbook/tutorial.xml index e704811..84d9df4 100644 --- a/doc/boostbook/tutorial.xml +++ b/doc/boostbook/tutorial.xml @@ -203,8 +203,8 @@ detected error:converted negative value to unsigned Programming by Contract is Too Slow Programming by Contract is a highly regarded technique. There has - been much written about it has been proposed as an addition to the C++ - language GarciaCrowl & + been much written about it and it has been proposed as an addition to the + C++ language GarciaCrowl & Ottosen. It (mostly) depends upon runtime checking of parameter and object values upon entry to and exit from every function. This can slow the program down considerably which in turn undermines the main @@ -224,10 +224,10 @@ detected error:converted negative value to unsigned In the example above the function convert incurs significant runtime cost every time the function is called. By using "safe" types, this cost - is moved to moment when the parameters are constructed. Depending on how - the program is constructed, this may totally eliminate extraneous - computations for parameter requirement type checking. In this scenario, - there is no reason to suppress the checking for release mode and our - program can be guaranteed to be always arithmetically correct. + is moved to moment when the parameters are constructed. This may totally + eliminate extraneous computations for parameter requirement type checking. + In this scenario, there is no reason to suppress the checking for release + mode and our program can be guaranteed to be always arithmetically + correct. diff --git a/examples/18F252_desktop.h b/examples/18F252_desktop.h index fe0ecff..1906da7 100644 --- a/examples/18F252_desktop.h +++ b/examples/18F252_desktop.h @@ -80,17 +80,16 @@ void ftest(){ return; } - #if ! defined(literal) // define a macro for literal types. This may not be strictly necessary // but it provides more information at compile time to the safe numerics // library which may result in faster code. -//#define literal(x) boost::numeric::safe_unsigned_literal{} -#define literal(x) x +#define literal(x) boost::numeric::safe_unsigned_literal{} +//#define literal(x) x #endif // make a 16 bit value from two 8 bit ones -int16 inline make16(int8 h, int8 l){ +uint16 inline make16(uint8 h, uint8 l){ return (h << literal(8)) | l; } diff --git a/examples/example1.cpp b/examples/example1.cpp index 35f9ba1..c1a1365 100644 --- a/examples/example1.cpp +++ b/examples/example1.cpp @@ -28,7 +28,7 @@ int main(int argc, const char * argv[]){ std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::numeric; - safe x = 127; + safe x = INT_MAX; safe y = 2; safe z; // rather than producing and invalid result an exception is thrown diff --git a/examples/example7.cpp b/examples/example7.cpp index 9ce3b0f..a587697 100644 --- a/examples/example7.cpp +++ b/examples/example7.cpp @@ -7,7 +7,7 @@ // NOT using safe numerics - enforce program contract explicitly // return total number of minutes -unsigned int convert( +unsigned int contract_convert( const unsigned int & hours, const unsigned int & minutes ) { @@ -26,90 +26,56 @@ unsigned int convert( // define convenient typenames for hours and minutes hh:mm using hours_t = boost::numeric::safe_unsigned_range<0, 23>; using minutes_t = boost::numeric::safe_unsigned_range<0, 59>; +using minutes_total_t = boost::numeric::safe_unsigned_range<0, 59>; // return total number of minutes // type returned is safe_unsigned_range<0, 24*60 - 1> -auto safe_convert(const hours_t & hours, const minutes_t & minutes) { - // no need for checking as parameters are guaranteed to be within limits - // expression below cannot throw ! zero runtime overhead +auto convert(const hours_t & hours, const minutes_t & minutes) { + // no need to test pre-conditions + // input parameters are guaranteed to hold legitimate values + // no need to test post-conditions + // return value guaranteed to hold result return hours * 60 + minutes; } +unsigned int test( + unsigned int hours, + unsigned int minutes +){ + // problem: checking of externally produced value can be expensive + // invalid parameters - detected - but at a heavy cost + return contract_convert(hours, minutes); + + // solution: use safe numerics + // safe types can be implicitly constructed base types + // construction guarentees corectness + // return value is known to fit in unsigned int + return convert(hours, minutes); + + // actually we don't even need the convert function any more + return hours_t(hours) * 60 + minutes_t(minutes); +} + int main(int argc, const char * argv[]){ std::cout << "example 8: "; std::cout << "enforce contracts with zero runtime cost" << std::endl; - std::cout << "Not using safe numerics" << std::endl; - // problem: checking of externally produced value can be expensive - try { - convert(10, 83); // invalid parameters - detected - but at a heavy cost - } - catch(std::exception e){ - std::cout << "exception thrown for parameter error" << std::endl; - } - - // solution: use safe range to restrict parameters - std::cout << "Using safe numerics" << std::endl; + unsigned int total_minutes; try { - // parameters are guaranteed to meet requirements - hours_t hours(10); - minutes_t minutes(83); // interrupt thrown here - // so the following will never fail - safe_convert(hours, minutes); + total_minutes = test(17, 83); } catch(std::exception e){ - std::cout - << "exception thrown when invalid arguments are constructed" - << std::endl; + std::cout << "parameter error detected" << std::endl; } try { - // parameters are guaranteed to meet requirements when - // constructed on the stack - safe_convert(hours_t(10), minutes_t(83)); + total_minutes = test(17, 10); } catch(std::exception e){ - std::cout - << "exception thrown when invalid arguments are constructed on the stack" - << std::endl; + // should never arrive here + std::cout << "parameter error erroneously detected" << std::endl; + return 1; } - - try { - // parameters are guaranteed to meet requirements when - // implicitly constructed to safe types to match function signature - safe_convert(10, 83); - } - catch(std::exception e){ - std::cout - << "exception thrown when invalid arguments are implicitly constructed" - << std::endl; - } - - try { - // the following will never throw as the values meet requirements. - const hours_t hours(10); - const minutes_t minutes(17); - - // note zero runtime overhead once values are constructed - - // the following will never throw because it cannot be called with - // invalid parameters - safe_convert(hours, minutes); // zero runtime overhead - - // since safe types can be converted to their underlying unsafe types - // we can still call an unsafe function with safe types - convert(hours, minutes); // zero (depending on compiler) runtime overhead - - // since unsafe types can be implicitly converted to corresponding - // safe types we can just pass the unsafe types. checking will occur - // when the safe type is constructed. - safe_convert(10, 17); // runtime cost in creating parameters - - } - catch(std::exception e){ - std::cout << "error detected!" << std::endl; - } - return 0; } diff --git a/include/automatic.hpp b/include/automatic.hpp index 1533e64..4a352a9 100644 --- a/include/automatic.hpp +++ b/include/automatic.hpp @@ -21,6 +21,7 @@ #include // (u)intmax_t, #include // true_type, false_type, is_same #include +#include #include "utility.hpp" #include "safe_common.hpp" @@ -325,17 +326,108 @@ struct automatic { } /////////////////////////////////////////////////////////////////////// + // temporary version +/* + template + struct left_shift_result { + // calculate the number of bits were going to need to hold + // the shifted result + constexpr static int result_base_digits = boost::static_signed_min< + // the largest number of bits possible + std::numeric_limits::digits, + // the largest number of bits that the shifted result might be + std::numeric_limits::digits + + // the number of bits in the max possible value of U + boost::static_signed_min< + // the largest number of bits of any type + std::numeric_limits::digits, + // the number bits to hold the maximum value of U + // static_cast(std::numeric_limits::max()) + std::numeric_limits::max() % std::numeric_limits::max() + >::value + >::value; + + using result_base_type = typename boost::mpl::if_c< + std::numeric_limits::is_signed, + typename boost::int_t::least, + typename boost::uint_t::least + >::type; + + 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()) + }; + constexpr static const interval 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 shifted result ! + 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{} + ; + using type = typename result_type< + result_base_type, + result_interval.l, + result_interval.u + >::type; + + }; +*/ template struct left_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()) }; + + 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; + using type = typename result_type< - T, - t_interval.l, - t_interval.u + r_base_type, + lower_bound, + upper_bound >::type; }; diff --git a/include/checked.hpp b/include/checked.hpp index 7ab06ef..d1874ea 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -627,61 +627,83 @@ constexpr modulus( namespace detail { +// INT34-C C++ + +// standard paragraph 5.8 / 2 +// The value of E1 << E2 is E1 left-shifted E2 bit positions; +// vacated bits are zero-filled. template typename std::enable_if< - ! std::numeric_limits::is_signed, + // If E1 has an unsigned type + ! std::numeric_limits::is_signed, checked_result >::type -constexpr check_shift( +constexpr checked_left_shift( const T & t, const U & u -) { - // INT34-C C++ standard paragraph 5.8 - if(u > std::numeric_limits::digits){ +) 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) + ){ + // behavior is undefined return checked_result( exception_type::domain_error, "shifting more bits than available is undefined behavior" ); } - // the following are prohibited by the standard. However - // 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 - if(t < 0){ - return checked_result( - exception_type::domain_error, - "shifting a negative value is undefined behavior" - ); - } - return cast(t); + + return cast(t) << u; } template typename std::enable_if< - std::numeric_limits::is_signed, + // if E1 has a signed type + std::numeric_limits::is_signed, checked_result >::type -constexpr check_shift( +constexpr checked_left_shift( const T & t, const U & u ) { - // INT34-C and C++ standard paragraph 5.8 - if(std::numeric_limits::max() > std::numeric_limits::digits){ - if(u > std::numeric_limits::digits){ - return checked_result( - exception_type::domain_error, - "shifting more bits than available is undefined behavior" - ); - } - } - // the following are prohibited by the standard. However - // 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 - if(u < 0){ + // 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 negative amount is undefined behavior" + "shifting more bits than available is undefined behavior" ); } if(t < 0){ @@ -690,59 +712,8 @@ constexpr check_shift( "shifting a negative value is undefined behavior" ); } - return cast(t); -} - -template -typename std::enable_if< - ! std::numeric_limits::is_signed, - checked_result ->::type -constexpr left_shift( - const R & r, - const U & u -){ - return static_cast(r << u); -} - -template -typename std::enable_if< - std::numeric_limits::is_signed, - checked_result ->::type -constexpr left_shift( - const R & r, - const U & u -){ - // INT13-C. Use bitwise operators only on unsigned operands - // cannot shift negative values to the left - return (r < 0) ? - checked_result( - exception_type::domain_error, - "shifting negative values off left is undefined" - ) - : - checked_result(r << u) - ; - - /* - // if a negative value is shifted left, then it could all of - // a sudden change sign - we inhibit this here. - if(r < 0){ - U ui = u; - R ri = r; - while(ui-- > 0){ - ri <<= 1; - if(ri >= 0){ - return checked_result( - exception_type::domain_error, - "shifting negative values off left is undefined" - ); - } - } - } - return r; - */ + // shift and convert the resulting value to the result type + return cast(t) << u; } } // detail @@ -752,43 +723,92 @@ template constexpr checked_result left_shift( const T & t, const U & u -) { - // INT13-C Note: We don't enforce recommendation as acually written - // as it would break too many programs. Specifically, we permit signed - // integer operands but require that they not be negative. This we can only - // enforce at runtime. - - const checked_result rx = detail::check_shift(t, u); - - if(! rx.no_exception()) - return rx; - - return detail::left_shift(rx, 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 + if(u < 0){ + return checked_result( + exception_type::domain_error, + "shifting negative amount is undefined behavior" + ); + } + if(t == 0) + return cast(0); + return detail::checked_left_shift(t, u); } +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 + ! std::numeric_limits::is_signed, + checked_result +>::type +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); +} + +template +typename std::enable_if< + // or if E1 has a signed type + std::numeric_limits::is_signed, + checked_result +>::type +constexpr checked_right_shift( + const T & t, + const U & u +) { + // and a non-negative value + if(t < 0){ + 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; +} + +} // detail + // right shift template constexpr checked_result right_shift( const T & t, const U & u ) { - // 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. - - const checked_result rx = detail::check_shift(t, u); - - if(! rx.no_exception()) - return rx; - - if(u > std::numeric_limits::digits){ + // 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 + if(u < 0){ return checked_result( exception_type::domain_error, - "shifting more bits than available is undefined behavior" + "shifting negative amount is undefined behavior" ); } - return static_cast(rx) >> u; + if(t == 0) + return cast(0); + return detail::checked_right_shift(t, u); } /////////////////////////////////// diff --git a/include/safe_base.hpp b/include/safe_base.hpp index fd5bfa4..56c3ed0 100644 --- a/include/safe_base.hpp +++ b/include/safe_base.hpp @@ -257,9 +257,12 @@ public: constexpr auto operator-() const { // unary minus return 0 - *this; } + constexpr auto operator+() const { // unary plus + return *this; + } template - constexpr auto operator~() const { // unary minus - return ~Stored(0) ^ *this; + constexpr auto operator~() const { // complement + return ~Stored(0u) ^ *this; } }; diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index 33d6bdf..07162de 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -251,7 +251,6 @@ struct common_promotion_policy { template struct addition_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -292,6 +291,7 @@ struct addition_result { static_cast>(r_interval) ; + using exception_policy = typename common_exception_policy::type; struct safe_type { using type = safe_base< result_base_type, @@ -379,7 +379,6 @@ constexpr inline operator+=(T & t, const U & u){ template struct subtraction_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -415,6 +414,7 @@ struct subtraction_result { static_cast>(r_interval) ; + using exception_policy = typename common_exception_policy::type; struct safe_type { using type = safe_base< result_base_type, @@ -502,7 +502,6 @@ constexpr inline operator-=(T & t, const U & u){ template struct multiplication_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -512,6 +511,7 @@ struct multiplication_result { u_base_type >::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 @@ -635,7 +635,6 @@ constexpr inline operator*=(T & t, const U & u){ template struct division_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -645,6 +644,7 @@ struct division_result { u_base_type >::type; + using exception_policy = typename common_exception_policy::type; struct safe_type { constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), @@ -773,7 +773,6 @@ constexpr inline operator/=(T & t, const U & u){ template struct modulus_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -784,6 +783,7 @@ struct modulus_result { >::type; + using exception_policy = typename common_exception_policy::type; struct safe_type { constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), @@ -813,6 +813,7 @@ struct modulus_result { : static_cast>(r_interval) ; + using type = safe_base< result_base_type, type_interval.l, @@ -1045,7 +1046,6 @@ constexpr operator<=(const T & lhs, const U & rhs) { // left shift template struct left_shift_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -1080,6 +1080,7 @@ struct left_shift_result { static_cast>(r_interval) ; + using exception_policy = typename common_exception_policy::type; using type = safe_base< result_base_type, type_interval.l, @@ -1124,6 +1125,13 @@ typename boost::lazy_enable_if_c< >::type constexpr inline operator<<(const T & t, const U & u){ // INT13-CPP + // C++ standards document N4618 & 5.8.2 + static_assert( + std::numeric_limits::is_integer, "shifted value must be an integer" + ); + static_assert( + std::numeric_limits::is_integer, "shift amount must be an integer" + ); using lsr = left_shift_result; return typename lsr::type( lsr::return_value( @@ -1150,7 +1158,6 @@ constexpr inline operator<<=(T & t, const U & u){ // right shift template struct right_shift_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -1185,6 +1192,7 @@ struct right_shift_result { static_cast>(r_interval) ; + using exception_policy = typename common_exception_policy::type; using type = safe_base< result_base_type, type_interval.l, @@ -1229,6 +1237,12 @@ typename boost::lazy_enable_if_c< >::type constexpr inline operator>>(const T & t, const U & u){ // INT13-CPP + static_assert( + std::numeric_limits::is_integer, "shifted value must be an integer" + ); + static_assert( + std::numeric_limits::is_integer, "shift amount must be an integer" + ); using rsr = right_shift_result; return typename rsr::type( rsr::return_value( @@ -1255,10 +1269,16 @@ constexpr inline operator>>=(T & t, const U & u){ ///////////////////////////////////////////////////////////////// // bitwise operators +// I considered making this illegal on signed integers because +// I'm thinking it doesn't make much sense from the point of +// view of an applications programmer. But after running some +// test on user code I see it's going to create problems. So +// I'm going to follow strictly the standard sections 5.11-5.13 +// related to bitwise operators + // operator | template struct bitwise_or_result { - using exception_policy = typename common_exception_policy::type; using promotion_policy = typename common_promotion_policy::type; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; @@ -1278,6 +1298,7 @@ struct bitwise_or_result { base_value(std::numeric_limits::max()) ; + using exception_policy = typename common_exception_policy::type; using type = safe_base< result_base_type, 0, @@ -1295,6 +1316,14 @@ typename boost::lazy_enable_if_c< bitwise_or_result >::type constexpr inline operator|(const T & t, const U & u){ + // see above + /* + static_assert( + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + "bitwise or on signed integer types is not always well defined" + ); + */ using bwr = bitwise_or_result; using result_base_type = typename bwr::result_base_type; @@ -1362,6 +1391,14 @@ typename boost::lazy_enable_if_c< bitwise_and_result >::type constexpr inline operator&(const T & t, const U & u){ + // see above + /* + static_assert( + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + "bitwise and on signed integer types is not always well defined" + ); + */ using bwr = bitwise_and_result; using result_base_type = typename bwr::result_base_type; @@ -1398,6 +1435,14 @@ typename boost::lazy_enable_if_c< bitwise_or_result >::type constexpr inline operator^(const T & t, const U & u){ + // see above + /* + static_assert( + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + "bitwise xor on signed integer types is not always well defined" + ); + */ using bwr = bitwise_or_result; using result_base_type = typename bwr::result_base_type; diff --git a/include/safe_literal.hpp b/include/safe_literal.hpp index ada1b03..13d6c36 100644 --- a/include/safe_literal.hpp +++ b/include/safe_literal.hpp @@ -112,8 +112,8 @@ using safe_signed_literal = safe_literal_impl< template< std::uintmax_t N, - class P = native, - class E = throw_exception + class P = void, + class E = void > using safe_unsigned_literal = safe_literal_impl< typename boost::numeric::unsigned_stored_type, @@ -132,4 +132,32 @@ using safe_unsigned_literal = safe_literal_impl< } // numeric } // boost +///////////////////////////////////////////////////////////////// +// numeric limits for safe etc. + +#include + +namespace std { + +template< + typename T, + T N, + class P, + class E +> +class numeric_limits > + : public std::numeric_limits +{ + using SL = boost::numeric::safe_literal_impl; +public: + constexpr static SL min() noexcept { + return SL(); + } + constexpr static SL max() noexcept { + return SL(); + } +}; + +} // std + #endif // BOOST_NUMERIC_SAFE_LITERAL_HPP diff --git a/include/utility.hpp b/include/utility.hpp index 7b7ecab..298c76c 100644 --- a/include/utility.hpp +++ b/include/utility.hpp @@ -18,21 +18,24 @@ namespace boost { namespace numeric { + // the number of bits required to render the value in x template typename std::enable_if< + // T being unsigned ! std::is_signed::value, - unsigned int + std::uintmax_t >::type constexpr log(T x){ - unsigned i = 0; + std::uintmax_t i = 0; for(; x > 0; ++i) x >>= 1; return i; } template typename std::enable_if< + // T being signed std::is_signed::value, - unsigned int + std::uintmax_t >::type constexpr log(T x){ if(x < 0) @@ -41,10 +44,12 @@ namespace numeric { static_cast::type>(x) ) + 1; } + // return type required to store a particular range template< std::intmax_t Min, std::intmax_t Max > + // signed range using signed_stored_type = typename boost::int_t< std::max({log(Min), log(Max)}) >::least ; @@ -53,11 +58,10 @@ namespace numeric { std::uintmax_t Min, std::uintmax_t Max > + // unsigned range using unsigned_stored_type = typename boost::uint_t< std::max({log(Min), log(Max)}) >::least ; - - } // numeric } // boost diff --git a/test/test_left_shift_automatic.cpp b/test/test_left_shift_automatic.cpp index 4d3ecc0..7942669 100644 --- a/test/test_left_shift_automatic.cpp +++ b/test/test_left_shift_automatic.cpp @@ -42,7 +42,7 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = { /*10*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*11*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*12*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", -/*13*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*13*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*14*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*15*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", @@ -64,8 +64,8 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = { /*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" +/*30*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", +/*31*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }; #include diff --git a/test/test_left_shift_native.cpp b/test/test_left_shift_native.cpp index 80a3012..59ef1a7 100644 --- a/test/test_left_shift_native.cpp +++ b/test/test_left_shift_native.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "../include/safe_integer.hpp" #include "../include/native.hpp" @@ -38,11 +39,11 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = { /* 7*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /* 8*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", -/* 9*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/* 9*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*10*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*11*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*12*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", -/*13*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx", +/*13*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*14*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*15*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", @@ -60,23 +61,24 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = { /*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", +/*26*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", +/*27*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", /*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" +/*30*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", +/*31*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }; #include -#define TEST_IMPL(v1, v2, result) \ - rval &= test_left_shift( \ - v1, \ - v2, \ - BOOST_PP_STRINGIZE(v1), \ - BOOST_PP_STRINGIZE(v2), \ - result \ + +#define TEST_IMPL(v1, v2, result) \ + rval &= test_left_shift( \ + v1, \ + v2, \ + BOOST_PP_STRINGIZE(v1), \ + BOOST_PP_STRINGIZE(v2), \ + result \ ); /**/ diff --git a/test/test_right_shift_automatic.cpp b/test/test_right_shift_automatic.cpp index 5cbe07f..5e38975 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*/ ".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", +/*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................", -/*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" +/*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................" }; #include diff --git a/test/test_right_shift_native.cpp b/test/test_right_shift_native.cpp index 7536418..8ebbcce 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*/ ".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", +/*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................", -/*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" +/*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................" }; #include