#ifndef BOOST_NUMERIC_CHECKED_HPP #define BOOST_NUMERIC_CHECKED_HPP // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // 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) // contains operations for doing checked aritmetic on NATIVE // C++ types. #include #include // is_fundamental, make_unsigned #include // std::max #include #include "safe_common.hpp" #include "checked_result.hpp" namespace boost { namespace numeric { namespace checked { //////////////////////////////////////////////////// // layer 0 - implement safe operations for intrinsic integers // Note presumption of twos complement integer arithmetic //////////////////////////////////////////////////// // safe casting on primitive types namespace detail { template struct cast_impl { template constexpr checked_result invoke(const T & t); }; template<> struct cast_impl { template constexpr static checked_result invoke(const T & t){ // INT32-C Ensure that operations on signed // integers do not overflow return t > std::numeric_limits::max() ? checked_result( exception_type::range_error, "converted signed value too large" ) : t < std::numeric_limits::min() ? checked_result( exception_type::range_error, "converted signed value too small" ) : checked_result(static_cast(t)) ; } }; template<> struct cast_impl { template constexpr static checked_result invoke(const T & t){ // INT30-C Ensure that unsigned integer operations // do not wrap return t > std::numeric_limits::max() ? checked_result( exception_type::range_error, "converted unsigned value too large" ) : checked_result(static_cast(t)) ; } }; template<> struct cast_impl { template constexpr static checked_result invoke(const T & t){ // INT32-C Ensure that operations on signed // integers do not overflow return t > std::numeric_limits::max() ? checked_result( exception_type::range_error, "converted unsigned value too large" ) : checked_result(static_cast(t)) ; } }; template<> struct cast_impl { template constexpr static checked_result invoke(const T & t){ return t < 0 ? checked_result( exception_type::range_error, "converted negative value to unsigned" ) : t > std::numeric_limits::max() ? checked_result( exception_type::range_error, "converted signed value too large" ) : checked_result(static_cast(t)) ; } }; } template constexpr checked_result cast( const T & t ) { return (! std::numeric_limits::is_integer) ? // conversions to floating point types are always OK checked_result(t) : (! std::numeric_limits::is_integer) ? // conversions to integer types // from floating point types are never OK checked_result( exception_type::range_error, "conversion of integer to float loses precision" ) : // for integer to integer conversions // it depends ... detail::cast_impl< std::numeric_limits::is_signed, std::numeric_limits::is_signed >::template invoke(t) ; } //////////////////////////////////////////////////// // safe addition on primitive types namespace detail { // result not an integer (float, double, etc) template typename boost::enable_if_c< ! std::numeric_limits::is_integer, checked_result >::type constexpr add( const R t, const R u ) { return t + u; } // result unsigned template typename boost::enable_if_c< std::numeric_limits::is_integer && ! std::numeric_limits::is_signed, checked_result >::type constexpr add( const R t, const R u ) { return // INT30-C. Ensure that unsigned integer operations do not wrap std::numeric_limits::max() - u < t ? checked_result( exception_type::overflow_error, "addition overflow" ) : checked_result(t + u) ; } // result signed template typename boost::enable_if_c< std::numeric_limits::is_integer && std::numeric_limits::is_signed, checked_result >::type constexpr add( const R t, const R u ) { 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))) ? checked_result( exception_type::overflow_error, "addition overflow" ) : checked_result(t + u) ; } } // namespace detail template constexpr checked_result add( const T & t, const U & u ) { static_assert(std::is_fundamental::value, "only intrinsic types permitted"); const checked_result rt(cast(t)); if(! rt.no_exception() ) return rt; const checked_result ru(cast(u)); if(! ru.no_exception() ) return ru; return detail::add(t, u); } //////////////////////////////////////////////////// // safe subtraction on primitive types namespace detail { // result not an integer (float, double, etc) template typename boost::enable_if_c< ! std::numeric_limits::is_integer, checked_result >::type constexpr subtract( const R t, const R u ) { return t - u; } // result unsigned template typename boost::enable_if_c< std::numeric_limits::is_integer && ! std::numeric_limits::is_signed, checked_result >::type constexpr subtract( const R t, const R u ) { // INT30-C. Ensure that unsigned integer operations do not wrap return t < u ? checked_result( exception_type::overflow_error, "subtraction overflow" ) : checked_result(t - u) ; } // result signed template typename boost::enable_if_c< std::numeric_limits::is_integer && std::numeric_limits::is_signed, checked_result >::type constexpr subtract( const R t, const R u ) { // 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))) ? checked_result( exception_type::overflow_error, "subtraction overflow" ) : checked_result(t - u) ; } } // namespace detail template constexpr checked_result subtract( const T & t, const U & u ) { static_assert(std::is_fundamental::value, "only intrinsic types permitted"); const checked_result rt(cast(t)); if(! rt.no_exception() ) return rt; const checked_result ru(cast(u)); if(! ru.no_exception() ) return ru; return detail::subtract(t, u); } //////////////////////////////////////////////////// // safe multiplication on primitive types namespace detail { // result is not an integer (ie float, double) template typename boost::enable_if_c< ! std::numeric_limits::is_integer, checked_result >::type constexpr multiply( const R t, const R u ){ return t * u; } // result unsigned template typename boost::enable_if_c< std::numeric_limits::is_integer && std::is_unsigned::value && (sizeof(R) <= (sizeof(std::uintmax_t) / 2)), checked_result >::type constexpr multiply( const R t, const R u ){ // INT30-C // fast method using intermediate result guaranteed not to overflow // todo - replace std::uintmax_t with a size double the size of R using i_type = std::uintmax_t; return static_cast(t) * static_cast(u) > std::numeric_limits::max() ? checked_result( exception_type::overflow_error, "multiplication overflow" ) : checked_result(t * u) ; } template typename boost::enable_if_c< std::numeric_limits::is_integer && std::is_unsigned::value && (sizeof(R) > sizeof(std::uintmax_t) / 2), checked_result >::type constexpr multiply( const R t, const R u ){ // INT30-C return u > 0 && t > std::numeric_limits::max() / u ? checked_result( exception_type::overflow_error, "multiplication overflow" ) : checked_result(t * u) ; } // result signed template typename boost::enable_if_c< std::numeric_limits::is_integer && std::is_signed::value && (sizeof(R) <= (sizeof(std::intmax_t) / 2)), checked_result >::type constexpr multiply( const R t, const R u ){ // INT30-C // fast method using intermediate result guaranteed not to overflow // todo - replace std::intmax_t with a size double the size of R using i_type = std::intmax_t; return ( static_cast(t) * static_cast(u) > static_cast(std::numeric_limits::max()) ) ? checked_result( exception_type::overflow_error, "multiplication overflow" ) : ( static_cast(t) * static_cast(u) < static_cast(std::numeric_limits::min()) ) ? checked_result( exception_type::underflow_error, "multiplication underflow" ) : checked_result(t * u) ; } template typename boost::enable_if_c< std::numeric_limits::is_integer && std::is_signed::value && (sizeof(R) > (sizeof(std::intmax_t) / 2)), checked_result >::type constexpr multiply( const R t, const R u ){ // INT32-C return t > 0 ? u > 0 ? t > std::numeric_limits::max() / u ? checked_result( exception_type::overflow_error, "multiplication overflow" ) : checked_result(t * u) : // u <= 0 u < std::numeric_limits::min() / t ? checked_result( exception_type::overflow_error, "multiplication overflow" ) : checked_result(t * u) : // t <= 0 u > 0 ? t < std::numeric_limits::min() / u ? checked_result( exception_type::overflow_error, "multiplication overflow" ) : checked_result(t * u) : // u <= 0 t != 0 && u < std::numeric_limits::max() / t ? checked_result( exception_type::overflow_error, "multiplication overflow" ) : checked_result(t * u) ; } } // namespace detail template constexpr checked_result multiply( const T & t, const U & u ) { static_assert(std::is_fundamental::value, "only intrinsic types permitted"); checked_result rt(cast(t)); if(! rt.no_exception() ) return rt; checked_result ru(cast(u)); if(! ru.no_exception() ) return ru; return detail::multiply(t, u); } //////////////////////////////// // safe division on unsafe types namespace detail { template typename boost::enable_if_c< !std::numeric_limits::is_signed, checked_result >::type constexpr divide( const R & t, const R & u ){ return t / u; } template typename boost::enable_if_c< std::numeric_limits::is_signed, checked_result >::type constexpr divide( const R & t, const R & u ){ return (u == -1 && t == std::numeric_limits::min()) ? checked_result( exception_type::domain_error, "result cannot be represented" ) : checked_result(t / u) ; } } // detail template checked_result constexpr divide( const T & t, const U & u ){ if(u == 0){ return checked_result( exception_type::domain_error, "divide by zero" ); } auto tx = cast(t); auto ux = cast(u); if(!tx.no_exception() || !ux.no_exception()) return checked_result( exception_type::overflow_error, "failure converting argument types" ); return detail::divide(tx.m_r, ux.m_r); } 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 template constexpr std::make_unsigned_t abs(const T & t){ return (t < 0 && t != std::numeric_limits::min()) ? -t : t ; } template checked_result constexpr modulus( const T & t, const U & u ) { static_assert(std::is_fundamental::value, "only intrinsic types permitted"); if(0 == u) return checked_result( exception_type::domain_error, "denominator is zero" ); return cast(abs(t) % abs(u)); } /////////////////////////////////// // shift operations namespace detail { template typename std::enable_if< ! std::numeric_limits::is_signed, checked_result >::type constexpr check_shift( const T & t, const U & u ) { // INT34-C C++ standard paragraph 5.8 if(u > std::numeric_limits::digits){ return checked_result( exception_type::domain_error, "shifting more bits than available is undefined behavior" ); } return cast(t); } template typename std::enable_if< std::numeric_limits::is_signed, checked_result >::type constexpr check_shift( const T & t, const U & u ) { // INT34-C 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" ); } } // INT34-C C++ standard paragraph 5.8 if(u < 0){ return checked_result( exception_type::domain_error, "shifting negative amount 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; */ } } // detail // left shift 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); } // 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){ return checked_result( exception_type::domain_error, "shifting more bits than available is undefined behavior" ); } return static_cast(rx) >> u; } /////////////////////////////////// // bitwise operations 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. template typename boost::enable_if< typename std::is_signed, bool >::type check_bitwise_operand(const T & t){ return t >= 0; } template typename boost::disable_if< typename std::is_signed, bool >::type check_bitwise_operand(const T & t){ return true; } } template constexpr checked_result bitwise_or( const T & t, const U & u ) { if(! detail::check_bitwise_operand(t)) return checked_result( exception_type::domain_error, "bitwise operands cannot be negative" ); const checked_result rt = cast(t); if(! rt.no_exception()) return rt; const checked_result ru = cast(u); if(! ru.no_exception()) return ru; return static_cast(ru) | static_cast(rt); } template constexpr checked_result bitwise_and( const T & t, const U & u ) { if(! detail::check_bitwise_operand(t)) return checked_result( exception_type::domain_error, "bitwise operands cannot be negative" ); const checked_result rt = cast(t); if(! rt.no_exception()) return rt; const checked_result ru = cast(u); if(! ru.no_exception()) return ru; return static_cast(ru) & static_cast(rt); } template constexpr checked_result bitwise_xor( const T & t, const U & u ) { if(! detail::check_bitwise_operand(t)) return checked_result( exception_type::domain_error, "bitwise operands cannot be negative" ); const checked_result rt = cast(t); if(! rt.no_exception()) return rt; const checked_result ru = cast(u); if(! ru.no_exception()) return ru; return static_cast(ru) ^ static_cast(rt); } } // checked } // numeric } // boost #endif // BOOST_NUMERIC__HPP