#ifndef BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS #define BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS // 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) // Implemenation of arithmetic on "extended" integers. // Extended integers are defined in terms of C++ primitive integers as // a) an interger range // b) extra elements +inf, -inf, indeterminate // // Integer operations are closed on the set of extended integers // but operations are not necessarily associative when they result in the // extensions +inf, -inf, and indeterminate // // in this code, the type "checked_result" where T is some // integer type is an "extended" integer. #include #include #include "checked_result.hpp" #include "checked_integer.hpp" ////////////////////////////////////////////////////////////////////////// // the following idea of "value_type" is used by several of the operations // defined by checked_result arithmetic. namespace boost { namespace safe_numerics { template constexpr void display(const boost::safe_numerics::checked_result & c){ switch(c.m_e){ case safe_numerics_error::success: std::terminate(); case safe_numerics_error::positive_overflow_error: // result is above representational maximum std::terminate(); case safe_numerics_error::negative_overflow_error: // result is below representational minimum std::terminate(); case safe_numerics_error::domain_error: // one operand is out of valid range std::terminate(); case safe_numerics_error::range_error: // result cannot be produced for this operation std::terminate(); case safe_numerics_error::precision_overflow_error: // result lost precision std::terminate(); case safe_numerics_error::underflow_error: // result is too small to be represented std::terminate(); case safe_numerics_error::negative_value_shift: // negative value in shift operator std::terminate(); case safe_numerics_error::negative_shift: // shift a negative value std::terminate(); case safe_numerics_error::shift_too_large: // l/r shift exceeds variable size std::terminate(); case safe_numerics_error::uninitialized_value: // creating of uninitialized value std::terminate(); } } ////////////////////////////////////////////////////////////////////////// // implement C++ operators for check_result struct sum_value_type { // characterization of various values const enum flag { known_value = 0, less_than_min, greater_than_max, indeterminate, count } m_flag; template constexpr flag to_flag(const checked_result & t) const { switch(static_cast(t)){ case safe_numerics_error::success: return known_value; case safe_numerics_error::negative_overflow_error: // result is below representational minimum return less_than_min; case safe_numerics_error::positive_overflow_error: // result is above representational maximum return greater_than_max; default: return indeterminate; } } template constexpr sum_value_type(const checked_result & t) : m_flag(to_flag(t)) {} constexpr operator std::uint8_t () const { return static_cast(m_flag); } }; // integers addition template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator+( const checked_result & t, const checked_result & u ){ using value_type = sum_value_type; const std::uint8_t order = static_cast(value_type::count); // note major pain. Clang constexpr multi-dimensional array is fine. // but gcc doesn't permit a multi-dimensional array to be be constexpr. // so we need to some ugly gymnastics to make our system work for all // all systems. const enum safe_numerics_error result[order * order] = { // t == known_value //{ // u == ... safe_numerics_error::success, // known_value, safe_numerics_error::negative_overflow_error, // less_than_min, safe_numerics_error::positive_overflow_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, // t == less_than_min, //{ // u == ... safe_numerics_error::negative_overflow_error, // known_value, safe_numerics_error::negative_overflow_error, // less_than_min, safe_numerics_error::range_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, // t == greater_than_max, //{ // u == ... safe_numerics_error::positive_overflow_error, // known_value, safe_numerics_error::range_error, // less_than_min, safe_numerics_error::positive_overflow_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, // t == indeterminate, //{ // u == ... safe_numerics_error::range_error, // known_value, safe_numerics_error::range_error, // less_than_min, safe_numerics_error::range_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, }; const value_type tx(t); const value_type ux(u); const safe_numerics_error e = result[tx * order + ux]; if(safe_numerics_error::success == e) return checked::add(t, u); return checked_result(e, "addition result"); } // unary + template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator+( const checked_result & t ){ return t; } // integers subtraction template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator-( const checked_result & t, const checked_result & u ){ using value_type = sum_value_type; constexpr const std::uint8_t order = static_cast(value_type::count); constexpr const enum safe_numerics_error result[order * order] = { // t == known_value //{ // u == ... safe_numerics_error::success, // known_value, safe_numerics_error::positive_overflow_error, // less_than_min, safe_numerics_error::negative_overflow_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, // t == less_than_min, //{ // u == ... safe_numerics_error::negative_overflow_error, // known_value, safe_numerics_error::range_error, // less_than_min, safe_numerics_error::negative_overflow_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, // t == greater_than_max, //{ // u == ... safe_numerics_error::positive_overflow_error, // known_value, safe_numerics_error::positive_overflow_error, // less_than_min, safe_numerics_error::range_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, // t == indeterminate, //{ // u == ... safe_numerics_error::range_error, // known_value, safe_numerics_error::range_error, // less_than_min, safe_numerics_error::range_error, // greater_than_max, safe_numerics_error::range_error, // indeterminate, //}, }; const value_type tx(t); const value_type ux(u); const safe_numerics_error e = result[tx * order + ux]; if(safe_numerics_error::success == e) return checked::subtract(t, u); return checked_result(e, "subtraction result"); } // unary - template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator-( const checked_result & t ){ // assert(false); return checked_result(0) - t; } struct product_value_type { // characterization of various values const enum flag { less_than_min = 0, less_than_zero, zero, greater_than_zero, greater_than_max, indeterminate, // count of number of cases for values count, // temporary values for special cases t_value, u_value, z_value } m_flag; template constexpr flag to_flag(const checked_result & t) const { switch(static_cast(t)){ case safe_numerics_error::success: return (t < checked_result(0)) ? less_than_zero : (t > checked_result(0)) ? greater_than_zero : zero; case safe_numerics_error::negative_overflow_error: // result is below representational minimum return less_than_min; case safe_numerics_error::positive_overflow_error: // result is above representational maximum return greater_than_max; default: return indeterminate; } } template constexpr product_value_type(const checked_result & t) : m_flag(to_flag(t)) {} constexpr operator std::uint8_t () const { return static_cast(m_flag); } }; // integers multiplication template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator*( const checked_result & t, const checked_result & u ){ using value_type = product_value_type; const std::uint8_t order = static_cast(value_type::count); constexpr const enum value_type::flag result[order * order] = { // t == less_than_min //{ // u == ... value_type::greater_than_max, // less_than_min, value_type::greater_than_max, // less_than_zero, value_type::zero, // zero, value_type::less_than_min, // greater_than_zero, value_type::less_than_min, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == less_than_zero, //{ // u == ... value_type::greater_than_max, // less_than_min, value_type::greater_than_zero, // less_than_zero, value_type::zero, // zero, value_type::less_than_zero, // greater_than_zero, value_type::less_than_min, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == zero, //{ // u == ... value_type::zero, // less_than_min, value_type::zero, // less_than_zero, value_type::zero, // zero, value_type::zero, // greater_than_zero, value_type::zero, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == greater_than_zero, //{ // u == ... value_type::less_than_min, // less_than_min, value_type::less_than_zero, // less_than_zero, value_type::zero, // zero, value_type::greater_than_zero, // greater_than_zero, value_type::greater_than_max, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == greater_than_max //{ value_type::less_than_min, // less_than_min, value_type::less_than_min, // less_than_zero, value_type::zero, // zero, value_type::greater_than_max, // greater_than_zero, value_type::greater_than_max, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == indeterminate //{ value_type::indeterminate, // less_than_min, value_type::indeterminate, // less_than_zero, value_type::indeterminate, // zero, value_type::indeterminate, // greater_than_zero, value_type::indeterminate, // greater than max, value_type::indeterminate, // indeterminate, //} }; const value_type tx(t); const value_type ux(u); switch(result[tx * order + ux]){ case value_type::less_than_min: return safe_numerics_error::negative_overflow_error; case value_type::zero: return T(0); case value_type::greater_than_max: return safe_numerics_error::positive_overflow_error; case value_type::less_than_zero: case value_type::greater_than_zero: return checked::multiply(t, u); case value_type::indeterminate: return safe_numerics_error::range_error; default: assert(false); } return checked_result(0); // to suppress msvc warning } // integers division template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator/( const checked_result & t, const checked_result & u ){ using value_type = product_value_type; const std::uint8_t order = static_cast(value_type::count); constexpr const enum value_type::flag result[order * order] = { // t == less_than_min //{ // u == ... value_type::indeterminate, // less_than_min, value_type::greater_than_max, // less_than_zero, value_type::less_than_min, // zero, value_type::less_than_min, // greater_than_zero, value_type::less_than_min, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == less_than_zero, //{ // u == ... value_type::zero, // less_than_min, value_type::greater_than_zero, // less_than_zero, value_type::less_than_min, // zero, value_type::less_than_zero, // greater_than_zero, value_type::zero, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == zero, //{ // u == ... value_type::zero, // less_than_min, value_type::zero, // less_than_zero, value_type::indeterminate, // zero, value_type::zero, // greater_than_zero, value_type::zero, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == greater_than_zero, //{ // u == ... value_type::zero, // less_than_min, value_type::less_than_zero, // less_than_zero, value_type::greater_than_max, // zero, value_type::greater_than_zero, // greater_than_zero, value_type::zero, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == greater_than_max //{ value_type::less_than_min, // less_than_min, value_type::less_than_min, // less_than_zero, value_type::greater_than_max, // zero, value_type::greater_than_max, // greater_than_zero, value_type::indeterminate, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == indeterminate //{ value_type::indeterminate, // less_than_min, value_type::indeterminate, // less_than_zero, value_type::indeterminate, // zero, value_type::indeterminate, // greater_than_zero, value_type::indeterminate, // greater than max, value_type::indeterminate, // indeterminate, //} }; const value_type tx(t); const value_type ux(u); switch(result[tx * order + ux]){ case value_type::less_than_min: return safe_numerics_error::negative_overflow_error; case value_type::zero: return 0; case value_type::greater_than_max: return safe_numerics_error::positive_overflow_error; case value_type::less_than_zero: case value_type::greater_than_zero: return checked::divide(t, u); case value_type::indeterminate: return safe_numerics_error::range_error; default: assert(false); } return checked_result(0); // to suppress msvc warning } // integers modulus template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator%( const checked_result & t, const checked_result & u ){ using value_type = product_value_type; const std::uint8_t order = static_cast(value_type::count); constexpr const enum value_type::flag result[order * order] = { // t == less_than_min //{ // u == ... value_type::indeterminate, // less_than_min, value_type::z_value, // less_than_zero, value_type::indeterminate, // zero, value_type::z_value, // greater_than_zero, value_type::indeterminate, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == less_than_zero, //{ // u == ... value_type::t_value, // less_than_min, value_type::greater_than_zero, // less_than_zero, value_type::indeterminate, // zero, value_type::less_than_zero, // greater_than_zero, value_type::t_value, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == zero, //{ // u == ... value_type::zero, // less_than_min, value_type::zero, // less_than_zero, value_type::indeterminate, // zero, value_type::zero, // greater_than_zero, value_type::zero, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == greater_than_zero, //{ // u == ... value_type::t_value, // less_than_min, value_type::less_than_zero, // less_than_zero, value_type::indeterminate, // zero, value_type::greater_than_zero, // greater_than_zero, value_type::t_value, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == greater_than_max //{ value_type::indeterminate, // less_than_min, value_type::u_value, // less_than_zero, value_type::indeterminate, // zero, value_type::u_value, // greater_than_zero, value_type::indeterminate, // greater than max, value_type::indeterminate, // indeterminate, //}, // t == indeterminate //{ value_type::indeterminate, // less_than_min, value_type::indeterminate, // less_than_zero, value_type::indeterminate, // zero, value_type::indeterminate, // greater_than_zero, value_type::indeterminate, // greater than max, value_type::indeterminate, // indeterminate, //} }; const value_type tx(t); const value_type ux(u); switch(result[tx * order + ux]){ case value_type::zero: return 0; case value_type::less_than_zero: case value_type::greater_than_zero: return checked::modulus(t, u); case value_type::indeterminate: return safe_numerics_error::range_error; case value_type::t_value: return t; case value_type::u_value: return checked::subtract(u, 1); case value_type::z_value: return checked::subtract(1, u); case value_type::greater_than_max: case value_type::less_than_min: default: assert(false); } // suppress msvc warning return checked_result(0); } // comparison operators template constexpr boost::logic::tribool operator<( const checked_result & t, const checked_result & u ){ using value_type = sum_value_type; constexpr const std::uint8_t order = static_cast(value_type::count); // the question arises about how to order values of type greater_than_min. // that is: what should greater_than_min < greater_than_min return. // // a) return indeterminate because we're talking about the "true" values for // which greater_than_min is a placholder. // // b) return false because the two values are "equal" // // for our purposes, a) seems the better interpretation. enum class result_type : std::uint8_t { runtime, false_value, true_value, indeterminate, }; constexpr const result_type resultx[order * order]{ // t == known_value //{ // u == ... result_type::runtime, // known_value, result_type::false_value, // less_than_min, result_type::true_value, // greater_than_max, result_type::indeterminate, // indeterminate, //}, // t == less_than_min //{ // u == ... result_type::true_value, // known_value, result_type::indeterminate, // less_than_min, see above argument result_type::true_value, // greater_than_max, result_type::indeterminate, // indeterminate, //}, // t == greater_than_max //{ // u == ... result_type::false_value, // known_value, result_type::false_value, // less_than_min, result_type::indeterminate, // greater_than_max, see above argument result_type::indeterminate, // indeterminate, //}, // t == indeterminate //{ // u == ... result_type::indeterminate, // known_value, result_type::indeterminate, // less_than_min, result_type::indeterminate, // greater_than_max, result_type::indeterminate, // indeterminate, //}, }; const value_type tx(t); const value_type ux(u); switch(resultx[tx * order + ux]){ case result_type::runtime: return static_cast(t) < static_cast(u); case result_type::false_value: return false; case result_type::true_value: return true; case result_type::indeterminate: return boost::logic::indeterminate; default: assert(false); } return true; } template constexpr boost::logic::tribool operator>=( const checked_result & t, const checked_result & u ){ return !(t < u); } template constexpr boost::logic::tribool operator>( const checked_result & t, const checked_result & u ){ return u < t; } template constexpr boost::logic::tribool operator<=( const checked_result & t, const checked_result & u ){ return !(u < t); } template constexpr boost::logic::tribool operator==( const checked_result & t, const checked_result & u ){ using value_type = sum_value_type; constexpr const std::uint8_t order = static_cast(value_type::count); enum class result_type : std::uint8_t { runtime, false_value, true_value, indeterminate, }; constexpr const result_type result[order * order]{ // t == known_value //{ // u == ... result_type::runtime, // known_value, result_type::false_value, // less_than_min, result_type::false_value, // greater_than_max, result_type::indeterminate, // indeterminate, //}, // t == less_than_min //{ // u == ... result_type::false_value, // known_value, result_type::indeterminate, // less_than_min, result_type::false_value, // greater_than_max, result_type::indeterminate, // indeterminate, //}, // t == greater_than_max //{ // u == ... result_type::false_value, // known_value, result_type::false_value, // less_than_min, result_type::indeterminate, // greater_than_max, result_type::indeterminate, // indeterminate, //}, // t == indeterminate //{ // u == ... result_type::indeterminate, // known_value, result_type::indeterminate, // less_than_min, result_type::indeterminate, // greater_than_max, result_type::indeterminate, // indeterminate, //}, }; const value_type tx(t); const value_type ux(u); switch(result[tx * order + ux]){ case result_type::runtime: return static_cast(t) == static_cast(u); case result_type::false_value: return false; case result_type::true_value: return true; case result_type::indeterminate: return boost::logic::indeterminate; default: assert(false); } // suppress msvc warning - not all control paths return a value return false; } template constexpr boost::logic::tribool operator!=( const checked_result & t, const checked_result & u ){ return ! (t == u); } template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator>>( const checked_result & t, const checked_result & u ); template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator~( const checked_result & t ){ // assert(false); return ~t.m_r; } template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator<<( const checked_result & t, const checked_result & u ){ using value_type = product_value_type; const std::uint8_t order = static_cast(value_type::count); constexpr const std::uint8_t result[order * order] = { // t == less_than_min //{ // u == ... 1, // -1, // less_than_min, 2, // safe_numerics_error::negative_overflow_error, // less_than_zero, 2, // safe_numerics_error::negative_overflow_error, // zero, 2, // safe_numerics_error::negative_overflow_error, // greater_than_zero, 2, // safe_numerics_error::negative_overflow_error, // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == less_than_zero, //{ // u == ... 3, // -1, // less_than_min, 4, // - (-t >> -u), // less_than_zero, 5, // safe_numerics_error::negative_overflow_error, // zero, 6, // - (-t << u), // greater_than_zero, 2, // safe_numerics_error::negative_overflow_error, // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == zero, //{ // u == ... 3, // 0 // less_than_min, 3, // 0 // less_than_zero, 3, // 0, // zero, 3, // 0, // greater_than_zero, 3, // 0, // greater than max, 3, // safe_numerics_error::range_error, // indeterminate, //}, // t == greater_than_zero, //{ // u == ... 3, // 0, // less_than_min, 7, // t << -u, // less_than_zero, 5, // t, // zero, 8, // t << u // greater_than_zero, 9, // safe_numerics_error::positive_overflow_error, // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == greater_than_max //{ // u == ... 1, // safe_numerics_error::range_error, // less_than_min, 9, // safe_numerics_error::positive_overflow_error), // less_than_zero, 9, // safe_numerics_error::positive_overflow_error, // zero, 9, // safe_numerics_error::positive_overflow_error), // greater_than_zero, 9, // safe_numerics_error::positive_overflow_error, // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == indeterminate //{ 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, //} }; const value_type tx(t); const value_type ux(u); assert(tx * order + ux < order * order); // I had a switch(i) statment here - but it results in an ICE // on multiple versions of gcc. So make the equivalent in // nested if statments - should be the same (more or less) // performancewise. const unsigned int i = result[tx * order + ux]; assert(i <= 9); if(1 == i){ return safe_numerics_error::range_error; } else if(2 == i){ return safe_numerics_error::negative_overflow_error; } else if(3 == i){ return checked_result(0); // the following gymnastics are to handle the case where // a value is changed from a negative to a positive number. // For example, and 8 bit number t == -128. Then -t also // equals -128 since 128 cannot be held in an 8 bit signed // integer. } else if(4 == i){ // - (-t >> -u) assert(static_cast(t < checked_result(0))); assert(static_cast(u < checked_result(0))); return t >> -u; } else if(5 == i){ return t; } else if(6 == i){ // - (-t << u) assert(static_cast(t < checked_result(0))); assert(static_cast(u > checked_result(0))); const checked_result temp_t = t * checked_result(2); const checked_result temp_u = u - checked_result(1); return - (-temp_t << temp_u); } else if(7 == i){ // t >> -u assert(static_cast(t > checked_result(0))); assert(static_cast(u < checked_result(0))); return t >> -u; } else if(8 == i){ // t << u assert(static_cast(t > checked_result(0))); assert(static_cast(u > checked_result(0))); checked_result r = checked::left_shift(t, u); return (r.m_e == safe_numerics_error::shift_too_large) ? checked_result(safe_numerics_error::positive_overflow_error) : r; } else if(9 == i){ return safe_numerics_error::positive_overflow_error; } else{ assert(false); }; return checked_result(0); // to suppress msvc warning } template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator>>( const checked_result & t, const checked_result & u ){ using value_type = product_value_type; const std::uint8_t order = static_cast(value_type::count); const std::uint8_t result[order * order] = { // t == less_than_min //{ // u == ... 2, // safe_numerics_error::negative_overflow_error, // less_than_min, 2, // safe_numerics_error::negative_overflow_error, // less_than_zero, 2, // safe_numerics_error::negative_overflow_error, // zero, 2, // safe_numerics_error::negative_overflow_error, // greater_than_zero, 1, // safe_numerics_error::range_error, // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == less_than_zero, //{ // u == ... 2, // safe_numerics_error::negative_overflow_error // less_than_min, 4, // - (-t << -u), // less_than_zero, 5, // safe_numerics_error::negative_overflow_error. // zero, 6, // - (-t >> u), // greater_than_zero, 3, // 0, ? or -1 // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == zero, //{ // u == ... 3, // 0 // less_than_min, 3, // 0 // less_than_zero, 3, // 0, // zero, 3, // 0, // greater_than_zero, 3, // 0, // greater than max, 3, // safe_numerics_error::range_error, // indeterminate, //}, // t == greater_than_zero, //{ // u == ... 9, // safe_numerics_error::positive_overflow_error // less_than_min, 7, // t << -u, // less_than_zero, 5, // t, // zero, 8, // t >> u // greater_than_zero, 3, // 0, // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == greater_than_max //{ // u == ... 9, // safe_numerics_error::positive_overflow_error, // less_than_min, 9, // safe_numerics_error::positive_overflow_error, // less_than_zero, 9, // safe_numerics_error::positive_overflow_error, // zero, 9, // safe_numerics_error::positive_overflow_error, // greater_than_zero, 1, // safe_numerics_error::range_error, // greater than max, 1, // safe_numerics_error::range_error, // indeterminate, //}, // t == indeterminate //{ 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, 1, // safe_numerics_error::range_error, // indeterminate, //} }; const value_type tx(t); const value_type ux(u); assert(tx * order + ux < order * order); // I had a switch(i) statment here - but it results in an ICE // on multiple versions of gcc. So make the equivalent in // nested if statments - should be the same (more or less) // performancewise. const unsigned int i = result[tx * order + ux]; assert(i <= 9); if(1 == i){ return safe_numerics_error::range_error; } else if(2 == i){ return safe_numerics_error::negative_overflow_error; } else if(3 == i){ return checked_result(0); } else if(4 == i){ // - (-t << -u) assert(static_cast(t < checked_result(0))); assert(static_cast(u < checked_result(0))); return t << -u; } else if(5 == i){ return t; } else if(6 == i){ // - (-t >> u) assert(static_cast(t < checked_result(0))); assert(static_cast(u > checked_result(0))); const checked_result temp_t = t / checked_result(2); const checked_result temp_u = u - checked_result(1); return - (-temp_t >> temp_u); } else if(7 == i){ // t << -u, assert(static_cast(t > checked_result(0))); assert(static_cast(u < checked_result(0))); return t << -u; } else if(8 == i){ // t >> u assert(static_cast(t > checked_result(0))); assert(static_cast(u > checked_result(0))); checked_result r = checked::right_shift(t, u); return (r.m_e == safe_numerics_error::shift_too_large) ? checked_result(0) : r; } else if(9 == i){ return safe_numerics_error::positive_overflow_error; } else{ assert(false); }; return checked_result(0); // to suppress msvc warning } template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator|( const checked_result & t, const checked_result & u ){ return t.exception() || u.exception() ? checked_result(safe_numerics_error::range_error) : checked::bitwise_or( static_cast(t), static_cast(u) ); } template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator^( const checked_result & t, const checked_result & u ){ return t.exception() || u.exception() ? checked_result(safe_numerics_error::range_error) : checked::bitwise_xor( static_cast(t), static_cast(u) ); } template typename std::enable_if< std::is_integral::value, checked_result >::type constexpr inline operator&( const checked_result & t, const checked_result & u ){ return t.exception() || u.exception() ? checked_result(safe_numerics_error::range_error) : checked::bitwise_and( static_cast(t), static_cast(u) ); } } // safe_numerics } // boost #include namespace std { template inline std::basic_ostream & operator<<( std::basic_ostream & os, const boost::safe_numerics::checked_result & r ){ if(!r.exception()) os << static_cast(r); else os << std::error_code(r.m_e).message() << ':' << r.m_msg; return os; } template inline std::basic_ostream & operator<<( std::basic_ostream & os, const boost::safe_numerics::checked_result & r ){ if(! r.exception()) os << static_cast(r); else os << std::error_code(r.m_e).message() << ':' << r.m_msg; return os; } template inline std::basic_ostream & operator<<( std::basic_ostream & os, const boost::safe_numerics::checked_result & r ){ if(! r.exception()) os << static_cast(r); else os << std::error_code(r.m_e).message() << ':' << r.m_msg; return os; } template inline std::basic_istream & operator>>( std::basic_istream & is, boost::safe_numerics::checked_result & r ){ is >> r.m_r; return is; } template inline std::basic_istream & operator>>( std::basic_istream & is, boost::safe_numerics::checked_result & r ){ std::int16_t i; is >> i; r.m_r = i; return is; } template inline std::basic_istream & operator>>( std::basic_istream & is, boost::safe_numerics::checked_result & r ){ std::uint16_t i; is >> i; r.m_r = i; return is; } } // std ///////////////////////////////////////////////////////////////// // numeric limits for checked #include namespace std { template class numeric_limits > : public std::numeric_limits { using this_type = boost::safe_numerics::checked_result; public: constexpr static this_type min() noexcept { return this_type(std::numeric_limits::min()); } constexpr static this_type max() noexcept { return this_type(std::numeric_limits::max()); } }; } // std #endif // BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS