123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813 |
- #ifndef BOOST_NUMERIC_CHECKED_INTEGER_HPP
- #define BOOST_NUMERIC_CHECKED_INTEGER_HPP
- // 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 <limits>
- #include <type_traits> // is_integral, make_unsigned, enable_if
- #include <algorithm> // std::max
- #include "checked_result.hpp"
- #include "checked_default.hpp"
- #include "safe_compare.hpp"
- #include "utility.hpp"
- #include "exception.hpp"
- namespace boost {
- namespace safe_numerics {
- // utility
- template<bool tf>
- using bool_type = typename std::conditional<tf, std::true_type, std::false_type>::type;
- ////////////////////////////////////////////////////
- // layer 0 - implement safe operations for intrinsic integers
- // Note presumption of twos complement integer arithmetic
- // convert an integral value to some other integral type
- template<
- typename R,
- R Min,
- R Max,
- typename T,
- class F
- >
- struct heterogeneous_checked_operation<
- R,
- Min,
- Max,
- T,
- F,
- typename std::enable_if<
- std::is_integral<R>::value
- && std::is_integral<T>::value
- >::type
- >{
- ////////////////////////////////////////////////////
- // safe casting on primitive types
- struct cast_impl_detail {
- constexpr static checked_result<R>
- cast_impl(
- const T & t,
- std::true_type, // R is signed
- std::true_type // T is signed
- ){
- // INT32-C Ensure that operations on signed
- // integers do not overflow
- return
- boost::safe_numerics::safe_compare::greater_than(t, Max) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "converted signed value too large"
- )
- : boost::safe_numerics::safe_compare::less_than(t, Min) ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "converted signed value too small"
- )
- :
- checked_result<R>(static_cast<R>(t))
- ;
- }
- constexpr static checked_result<R>
- cast_impl(
- const T & t,
- std::true_type, // R is signed
- std::false_type // T is unsigned
- ){
- // INT30-C Ensure that unsigned integer operations
- // do not wrap
- return
- boost::safe_numerics::safe_compare::greater_than(t, Max) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "converted unsigned value too large"
- )
- :
- boost::safe_numerics::safe_compare::less_than(t, Min) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "converted unsigned value too small"
- )
- :
- checked_result<R>(static_cast<R>(t))
- ;
- }
- constexpr static checked_result<R>
- cast_impl(
- const T & t,
- std::false_type, // R is unsigned
- std::false_type // T is unsigned
- ){
- // INT32-C Ensure that operations on unsigned
- // integers do not overflow
- return
- boost::safe_numerics::safe_compare::greater_than(t, Max) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "converted unsigned value too large"
- )
- :
- boost::safe_numerics::safe_compare::less_than(t, Min) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "converted unsigned value too small"
- )
- :
- checked_result<R>(static_cast<R>(t))
- ;
- }
- constexpr static checked_result<R>
- cast_impl(
- const T & t,
- std::false_type, // R is unsigned
- std::true_type // T is signed
- ){
- return
- boost::safe_numerics::safe_compare::less_than(t, Min) ?
- F::template invoke<safe_numerics_error::domain_error>(
- "converted value to low or negative"
- )
- :
- boost::safe_numerics::safe_compare::greater_than(t, Max) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "converted signed value too large"
- )
- :
- checked_result<R>(static_cast<R>(t))
- ;
- }
- }; // cast_impl_detail
- constexpr static checked_result<R>
- cast(const T & t){
- return
- cast_impl_detail::cast_impl(
- t,
- std::is_signed<R>(),
- std::is_signed<T>()
- );
- }
- }; // heterogeneous_checked_operation
- // converting floating point value to integral type
- template<
- typename R,
- R Min,
- R Max,
- typename T,
- class F
- >
- struct heterogeneous_checked_operation<
- R,
- Min,
- Max,
- T,
- F,
- typename std::enable_if<
- std::is_integral<R>::value
- && std::is_floating_point<T>::value
- >::type
- >{
- constexpr static checked_result<R>
- cast(const T & t){
- return static_cast<R>(t);
- }
- }; // heterogeneous_checked_operation
- // converting integral value to floating point type
- // INT35-C. Use correct integer precisions
- template<
- typename R,
- R Min,
- R Max,
- typename T,
- class F
- >
- struct heterogeneous_checked_operation<
- R,
- Min,
- Max,
- T,
- F,
- typename std::enable_if<
- std::is_floating_point<R>::value
- && std::is_integral<T>::value
- >::type
- >{
- constexpr static checked_result<R>
- cast(const T & t){
- if(std::numeric_limits<R>::digits < std::numeric_limits<T>::digits){
- if(utility::significant_bits(t) > std::numeric_limits<R>::digits){
- return F::invoke(
- safe_numerics_error::precision_overflow_error,
- "keep precision"
- );
- }
- }
- return t;
- }
- }; // heterogeneous_checked_operation
- // binary operations on primitive integer types
- template<
- typename R,
- class F
- >
- struct checked_operation<R, F,
- typename std::enable_if<
- std::is_integral<R>::value
- >::type
- >{
- ////////////////////////////////////////////////////
- // safe addition on primitive types
- struct add_impl_detail {
- // result unsigned
- constexpr static checked_result<R> add(
- const R t,
- const R u,
- std::false_type // R unsigned
- ){
- return
- // INT30-C. Ensure that unsigned integer operations do not wrap
- std::numeric_limits<R>::max() - u < t ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "addition result too large"
- )
- :
- checked_result<R>(t + u)
- ;
- }
- // result signed
- constexpr static checked_result<R> add(
- const R t,
- const R u,
- std::true_type // R signed
- ){
- // INT32-C. Ensure that operations on signed integers do not result in overflow
- return
- // INT32-C. Ensure that operations on signed integers do not result in overflow
- ((u > 0) && (t > (std::numeric_limits<R>::max() - u))) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "addition result too large"
- )
- :
- ((u < 0) && (t < (std::numeric_limits<R>::min() - u))) ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "addition result too low"
- )
- :
- checked_result<R>(t + u)
- ;
- }
- }; // add_impl_detail
- constexpr static checked_result<R>
- add(const R & t, const R & u){
- return add_impl_detail::add(t, u, std::is_signed<R>());
- }
- ////////////////////////////////////////////////////
- // safe subtraction on primitive types
- struct subtract_impl_detail {
- // result unsigned
- constexpr static checked_result<R> subtract(
- const R t,
- const R u,
- std::false_type // R is unsigned
- ){
- // INT30-C. Ensure that unsigned integer operations do not wrap
- return
- t < u ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "subtraction result cannot be negative"
- )
- :
- checked_result<R>(t - u)
- ;
- }
- // result signed
- constexpr static checked_result<R> subtract(
- const R t,
- const R u,
- std::true_type // R is signed
- ){ // INT32-C
- return
- // INT32-C. Ensure that operations on signed integers do not result in overflow
- ((u > 0) && (t < (std::numeric_limits<R>::min() + u))) ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "subtraction result overflows result type"
- )
- :
- ((u < 0) && (t > (std::numeric_limits<R>::max() + u))) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "subtraction result overflows result type"
- )
- :
- checked_result<R>(t - u)
- ;
- }
- }; // subtract_impl_detail
- constexpr static checked_result<R> subtract(const R & t, const R & u){
- return subtract_impl_detail::subtract(t, u, std::is_signed<R>());
- }
- ////////////////////////////////////////////////////
- // safe minus on primitive types
- struct minus_impl_detail {
- // result unsigned
- constexpr static checked_result<R> minus(
- const R t,
- std::false_type // R is unsigned
- ){
- return t > 0 ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "minus unsigned would be negative"
- )
- :
- // t == 0
- checked_result<R>(0)
- ;
- }
- // result signed
- constexpr static checked_result<R> minus(
- const R t,
- std::true_type // R is signed
- ){ // INT32-C
- return t == std::numeric_limits<R>::min() ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "subtraction result overflows result type"
- )
- :
- checked_result<R>(-t)
- ;
- }
- }; // minus_impl_detail
- constexpr static checked_result<R> minus(const R & t){
- return minus_impl_detail::minus(t, std::is_signed<R>());
- }
- ////////////////////////////////////////////////////
- // safe multiplication on primitive types
- struct multiply_impl_detail {
- // result unsigned
- constexpr static checked_result<R> multiply(
- const R t,
- const R u,
- std::false_type, // R is unsigned
- std::false_type // !(sizeochecked_result<R>R) > sizeochecked_result<R>std::uintmax_t) / 2)
- ){
- // 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<i_type>(t) * static_cast<i_type>(u)
- > std::numeric_limits<R>::max() ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "multiplication overflow"
- )
- :
- checked_result<R>(t * u)
- ;
- }
- constexpr static checked_result<R> multiply(
- const R t,
- const R u,
- std::false_type, // R is unsigned
- std::true_type // (sizeochecked_result<R>R) > sizeochecked_result<R>std::uintmax_t) / 2)
- ){
- // INT30-C
- return
- u > 0 && t > std::numeric_limits<R>::max() / u ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "multiplication overflow"
- )
- :
- checked_result<R>(t * u)
- ;
- }
- // result signed
- constexpr static checked_result<R> multiply(
- const R t,
- const R u,
- std::true_type, // R is signed
- std::false_type // ! (sizeochecked_result<R>R) > (sizeochecked_result<R>std::intmax_t) / 2))
- ){
- // 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<i_type>(t) * static_cast<i_type>(u)
- > static_cast<i_type>(std::numeric_limits<R>::max())
- ) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "multiplication overflow"
- )
- :
- (
- static_cast<i_type>(t) * static_cast<i_type>(u)
- < static_cast<i_type>(std::numeric_limits<R>::min())
- ) ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "multiplication overflow"
- )
- :
- checked_result<R>(t * u)
- ;
- }
- constexpr static checked_result<R> multiply(
- const R t,
- const R u,
- std::true_type, // R is signed
- std::true_type // (sizeochecked_result<R>R) > (sizeochecked_result<R>std::intmax_t) / 2))
- ){ // INT32-C
- return t > 0 ?
- u > 0 ?
- t > std::numeric_limits<R>::max() / u ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "multiplication overflow"
- )
- :
- checked_result<R>(t * u)
- : // u <= 0
- u < std::numeric_limits<R>::min() / t ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "multiplication overflow"
- )
- :
- checked_result<R>(t * u)
- : // t <= 0
- u > 0 ?
- t < std::numeric_limits<R>::min() / u ?
- F::template invoke<safe_numerics_error::negative_overflow_error>(
- "multiplication overflow"
- )
- :
- checked_result<R>(t * u)
- : // u <= 0
- t != 0 && u < std::numeric_limits<R>::max() / t ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "multiplication overflow"
- )
- :
- checked_result<R>(t * u)
- ;
- }
- }; // multiply_impl_detail
- constexpr static checked_result<R> multiply(const R & t, const R & u){
- return multiply_impl_detail::multiply(
- t,
- u,
- std::is_signed<R>(),
- std::integral_constant<
- bool,
- (sizeof(R) > sizeof(std::uintmax_t) / 2)
- >()
- );
- }
- ////////////////////////////////
- // safe division on unsafe types
- struct divide_impl_detail {
- constexpr static checked_result<R> divide(
- const R & t,
- const R & u,
- std::false_type // R is unsigned
- ){
- return t / u;
- }
- constexpr static checked_result<R> divide(
- const R & t,
- const R & u,
- std::true_type // R is signed
- ){
- return
- (u == -1 && t == std::numeric_limits<R>::min()) ?
- F::template invoke<safe_numerics_error::positive_overflow_error>(
- "result cannot be represented"
- )
- :
- checked_result<R>(t / u)
- ;
- }
- }; // divide_impl_detail
- // note that we presume that the size of R >= size of T
- constexpr static checked_result<R> divide(const R & t, const R & u){
- if(u == 0){
- return F::template invoke<safe_numerics_error::domain_error>(
- "divide by zero"
- );
- }
- return divide_impl_detail::divide(t, u, std::is_signed<R>());
- }
- ////////////////////////////////
- // safe modulus on unsafe types
- struct modulus_impl_detail {
- constexpr static checked_result<R> modulus(
- const R & t,
- const R & u,
- std::false_type // R is unsigned
- ){
- return t % u;
- }
- constexpr static checked_result<R> modulus(
- const R & t,
- const R & u,
- std::true_type // R is signed
- ){
- if(u >= 0)
- return t % u;
- checked_result<R> ux = checked::minus(u);
- if(ux.exception())
- return t;
- return t % static_cast<R>(ux);
- }
- }; // modulus_impl_detail
- constexpr static checked_result<R> modulus(const R & t, const R & u){
- if(0 == u)
- return F::template invoke<safe_numerics_error::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 modulus_impl_detail::modulus(t, u, typename std::is_signed<R>::type());
- }
- ///////////////////////////////////
- // shift operations
- struct left_shift_integer_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<typename T>
- 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.
- constexpr static checked_result<R> left_shift(
- const R & t,
- const R & u,
- std::false_type // R is unsigned
- ){
- // the value of the result is E1 x 2^E2, reduced modulo one more than
- // the maximum value representable in the result type.
- // see 5.8 & 1
- // if right operand is
- // greater than or equal to the length in bits of the promoted left operand.
- if(
- safe_compare::greater_than(
- u,
- std::numeric_limits<R>::digits - utility::significant_bits(t)
- )
- ){
- // behavior is undefined
- return F::template invoke<safe_numerics_error::shift_too_large>(
- "shifting left more bits than available is undefined behavior"
- );
- }
- return t << u;
- }
- constexpr static checked_result<R> left_shift(
- const R & t,
- const R & u,
- std::true_type // R is signed
- ){
- // and [E1] has a non-negative value
- if(t >= 0){
- // and E1 x 2^E2 is representable in the corresponding
- // unsigned type of the result type,
- // see 5.8 & 1
- // if right operand is
- // greater than or equal to the length in bits of the promoted left operand.
- if(
- safe_compare::greater_than(
- u,
- std::numeric_limits<R>::digits - utility::significant_bits(t)
- )
- ){
- // behavior is undefined
- return F::template invoke<safe_numerics_error::shift_too_large>(
- "shifting left more bits than available"
- );
- }
- else{
- return t << u;
- }
- }
- // otherwise, the behavior is undefined.
- return F::template invoke<safe_numerics_error::negative_shift>(
- "shifting a negative value"
- );
- }
- }; // left_shift_integer_detail
- constexpr static checked_result<R> left_shift(
- const R & t,
- const R & u
- ){
- // INT34-C - Do not shift an expression by a negative number of bits
- // standard paragraph 5.8 & 1
- // if the right operand is negative
- if(u == 0){
- return t;
- }
- if(u < 0){
- return F::template invoke<safe_numerics_error::negative_shift>(
- "shifting negative amount"
- );
- }
- if(u > std::numeric_limits<R>::digits){
- // behavior is undefined
- return F::template invoke<safe_numerics_error::shift_too_large>(
- "shifting more bits than available"
- );
- }
- return left_shift_integer_detail::left_shift(t, u, std::is_signed<R>());
- }
- // right shift
- struct right_shift_integer_detail {
- // INT34-C C++
- // standard paragraph 5.8 / 3
- // The value of E1 << E2 is E1 left-shifted E2 bit positions;
- // vacated bits are zero-filled.
- constexpr static checked_result<R> right_shift(
- const R & t,
- const R & u,
- std::false_type // T is unsigned
- ){
- // the value of the result is the integral part of the
- // quotient of E1/2E2
- return t >> u;
- }
- constexpr static checked_result<R> right_shift(
- const R & t,
- const R & u,
- std::true_type // T is signed;
- ){
- if(t < 0){
- // note that the C++ standard considers this case is "implemenation
- // defined" rather than "undefined".
- return F::template invoke<safe_numerics_error::negative_value_shift>(
- "shifting a negative value"
- );
- }
- // the value is the integral part of E1 / 2^E2,
- return t >> u;
- }
- }; // right_shift_integer_detail
- constexpr static checked_result<R> right_shift(
- const R & t,
- const R & u
- ){
- // INT34-C - Do not shift an expression by a negative number of bits
- // standard paragraph 5.8 & 1
- // if the right operand is negative
- if(u < 0){
- return F::template invoke<safe_numerics_error::negative_shift>(
- "shifting negative amount"
- );
- }
- if(u > std::numeric_limits<R>::digits){
- // behavior is undefined
- return F::template invoke<safe_numerics_error::shift_too_large>(
- "shifting more bits than available"
- );
- }
- return right_shift_integer_detail::right_shift(t, u ,std::is_signed<R>());
- }
- ///////////////////////////////////
- // 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.
- constexpr static checked_result<R> bitwise_or(const R & t, const R & u){
- using namespace boost::safe_numerics::utility;
- const unsigned int result_size
- = std::max(significant_bits(t), significant_bits(u));
- if(result_size > bits_type<R>::value){
- return F::template invoke<safe_numerics_error::positive_overflow_error>(
- "result type too small to hold bitwise or"
- );
- }
- return t | u;
- }
- constexpr static checked_result<R> bitwise_xor(const R & t, const R & u){
- using namespace boost::safe_numerics::utility;
- const unsigned int result_size
- = std::max(significant_bits(t), significant_bits(u));
- if(result_size > bits_type<R>::value){
- return F::template invoke<safe_numerics_error::positive_overflow_error>(
- "result type too small to hold bitwise or"
- );
- }
- return t ^ u;
- }
- constexpr static checked_result<R> bitwise_and(const R & t, const R & u){
- using namespace boost::safe_numerics::utility;
- const unsigned int result_size
- = std::min(significant_bits(t), significant_bits(u));
- if(result_size > bits_type<R>::value){
- return F::template invoke<safe_numerics_error::positive_overflow_error>(
- "result type too small to hold bitwise and"
- );
- }
- return t & u;
- }
- constexpr static checked_result<R> bitwise_not(const R & t){
- using namespace boost::safe_numerics::utility;
- if(significant_bits(t) > bits_type<R>::value){
- return F::template invoke<safe_numerics_error::positive_overflow_error>(
- "result type too small to hold bitwise inverse"
- );
- }
- return ~t;
- }
- }; // checked_operation
- } // safe_numerics
- } // boost
- #endif // BOOST_NUMERIC_CHECKED_INTEGER_HPP
|