#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" #include "utility.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) noexcept { // INT32-C Ensure that operations on signed // integers do not overflow return t > std::numeric_limits::max() ? checked_result( exception_type::positive_overflow_error, "converted signed value too large" ) : t < std::numeric_limits::min() ? checked_result( exception_type::negative_overflow_error, "converted signed value too small" ) : checked_result(static_cast(t)) ; } }; template<> struct cast_impl { template constexpr static checked_result invoke(const T & t) noexcept { // INT30-C Ensure that unsigned integer operations // do not wrap return t > std::numeric_limits::max() ? checked_result( exception_type::positive_overflow_error, "converted unsigned value too large" ) : checked_result(static_cast(t)) ; } }; template<> struct cast_impl { template constexpr static checked_result invoke(const T & t) noexcept { // INT32-C Ensure that operations on unsigned // integers do not overflow return t > std::numeric_limits::max() ? checked_result( exception_type::positive_overflow_error, "converted unsigned value too large" ) : checked_result(static_cast(t)) ; } }; template<> struct cast_impl { template constexpr static checked_result invoke(const T & t) noexcept { return t < 0 ? checked_result( exception_type::domain_error, "converted negative value to unsigned" ) : t > std::numeric_limits::max() ? checked_result( exception_type::positive_overflow_error, "converted signed value too large" ) : checked_result(static_cast(t)) ; } }; } template constexpr checked_result cast( const T & t ) noexcept { 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::domain_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 ) noexcept { 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 ) noexcept { return // INT30-C. Ensure that unsigned integer operations do not wrap std::numeric_limits::max() - u < t ? checked_result( exception_type::positive_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 ) noexcept { return // INT32-C. Ensure that operations on signed integers do not result in overflow ((u > 0) && (t > (std::numeric_limits::max() - u))) ? checked_result( 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) ; } } // namespace detail template constexpr checked_result add( const T & t, const U & u ) noexcept { static_assert(std::is_fundamental::value, "only intrinsic types permitted"); const checked_result rt(cast(t)); if(rt.exception() ) return rt; const checked_result ru(cast(u)); if(ru.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 ) noexcept { 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 ) noexcept { // INT30-C. Ensure that unsigned integer operations do not wrap return t < u ? checked_result( exception_type::range_error, "subtraction result cannot be negative" ) : 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 ) noexcept { // INT32-C return // INT32-C. Ensure that operations on signed integers do not result in overflow ((u > 0) && (t < (std::numeric_limits::min() + u))) ? checked_result( 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) ; } } // namespace detail template constexpr checked_result subtract( const T & t, const U & u ) noexcept { 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 ) noexcept { 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 ) noexcept { // 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::positive_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 ) noexcept { // INT30-C return u > 0 && t > std::numeric_limits::max() / u ? checked_result( exception_type::positive_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 ) noexcept { // 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::positive_overflow_error, "multiplication overflow" ) : ( static_cast(t) * static_cast(u) < static_cast(std::numeric_limits::min()) ) ? checked_result( exception_type::negative_overflow_error, "multiplication overflow" ) : 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 ) noexcept { // INT32-C return t > 0 ? u > 0 ? t > std::numeric_limits::max() / u ? checked_result( exception_type::positive_overflow_error, "multiplication overflow" ) : checked_result(t * u) : // u <= 0 u < std::numeric_limits::min() / t ? checked_result( exception_type::negative_overflow_error, "multiplication overflow" ) : checked_result(t * u) : // t <= 0 u > 0 ? t < std::numeric_limits::min() / u ? checked_result( exception_type::negative_overflow_error, "multiplication overflow" ) : checked_result(t * u) : // u <= 0 t != 0 && u < std::numeric_limits::max() / t ? checked_result( exception_type::positive_overflow_error, "multiplication overflow" ) : checked_result(t * u) ; } } // namespace detail template constexpr checked_result multiply( const T & t, const U & u ) noexcept { 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 ) noexcept { 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 ) noexcept { return (u == -1 && t == std::numeric_limits::min()) ? checked_result( exception_type::domain_error, "result cannot be represented" ) : checked_result(t / u) ; } } // detail // note that we presume that the size of R >= size of T template checked_result constexpr divide( const T & t, const U & u ) noexcept { if(u == 0){ return checked_result( exception_type::domain_error, "divide by zero" ); } checked_result tx = cast(t); checked_result ux = cast(u); if(tx.exception() || ux.exception()) return checked_result( exception_type::domain_error, "failure converting argument types" ); return detail::divide(tx, ux); } //////////////////////////////// // safe modulus on unsafe types // built-in abs isn't constexpr - so fix this here template constexpr std::make_unsigned_t abs(const T & t) noexcept { return (t < 0 && t != std::numeric_limits::min()) ? -t : t ; } template checked_result constexpr modulus( const T & t, const U & u ) noexcept { static_assert(std::is_fundamental::value, "only intrinsic types permitted"); if(0 == u) return checked_result( exception_type::domain_error, "denominator is zero" ); // 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 // and provokes a hardware exception. We can fix this using abs() // since -128 % -1 = -128 % 1 = 0 return t % abs(u); } /////////////////////////////////// // shift operations namespace detail { #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 // 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< // If E1 has an unsigned type ! std::numeric_limits::is_signed, checked_result >::type constexpr checked_left_shift( const T & t, const U & u ) noexcept { // the value of the result is E1 x 2^E2, reduced modulo one more than // the maximum value representable in the result type. // 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 > std::numeric_limits::digits - utility::significant_bits(t)){ // behavior is undefined return checked_result( exception_type::domain_error, "shifting more bits than available is undefined behavior" ); } return t << u; } template typename std::enable_if< // if E1 has a signed type std::numeric_limits::is_signed, checked_result >::type constexpr checked_left_shift( const T & t, const U & u ) noexcept { if(t < 0){ return checked_result( exception_type::domain_error, "shifting a negative value is undefined behavior" ); } // 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 template constexpr checked_result left_shift( const T & t, const U & u ) noexcept { // 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, "shifting negative amount is undefined behavior" ); } if(t == 0) return cast(t); return detail::checked_left_shift(t, u); } // right shift namespace detail { 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 { 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 ) noexcept { // 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" ); } // 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 ) noexcept { // 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 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) return cast(0); return detail::checked_right_shift(t, u); } /////////////////////////////////// // bitwise operations // INT13-C Note: We don't enforce recommendation as acually written // as it would break too many programs. Specifically, we permit signed // integer operands. template constexpr checked_result bitwise_or( const T & t, const U & u ) noexcept { using namespace boost::numeric::utility; /* if(! detail::check_bitwise_operand(t)) return checked_result( exception_type::domain_error, "bitwise operands cannot be negative" ); */ const unsigned int result_size = std::max(significant_bits(t), significant_bits(u)); if(result_size > bits_type::value){ return checked_result{ exception_type::positive_overflow_error, "result type too small to hold bitwise or" }; } /* 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); */ return t | u; } template constexpr checked_result bitwise_xor( const T & t, const U & u ) noexcept { using namespace boost::numeric::utility; /* if(! detail::check_bitwise_operand(t)) return checked_result( exception_type::domain_error, "bitwise operands cannot be negative" ); */ const unsigned int result_size = std::max(significant_bits(t), significant_bits(u)); if(result_size > bits_type::value){ return checked_result{ exception_type::positive_overflow_error, "result type too small to hold bitwise or" }; } 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 ) noexcept { using namespace boost::numeric::utility; /* if(! detail::check_bitwise_operand(t)) return checked_result( exception_type::domain_error, "bitwise operands cannot be negative" ); */ const unsigned int result_size = std::min(significant_bits(t), significant_bits(u)); if(result_size > bits_type::value){ return checked_result{ exception_type::positive_overflow_error, "result type too small to hold bitwise or" }; } /* 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); */ return t & u; } } // checked } // numeric } // boost #endif // BOOST_NUMERIC__HPP