ext-boost/boost/safe_numerics/checked_result_operations.hpp

1218 lines
42 KiB
C++
Raw Normal View History

#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<T>" where T is some
// integer type is an "extended" integer.
#include <cassert>
#include <boost/logic/tribool.hpp>
#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<typename T>
constexpr void display(const boost::safe_numerics::checked_result<T> & 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<T>
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<class T>
constexpr flag to_flag(const checked_result<T> & t) const {
switch(static_cast<safe_numerics_error>(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<class T>
constexpr sum_value_type(const checked_result<T> & t) :
m_flag(to_flag(t))
{}
constexpr operator std::uint8_t () const {
return static_cast<std::uint8_t>(m_flag);
}
};
// integers addition
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator+(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = sum_value_type;
const std::uint8_t order = static_cast<std::uint8_t>(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>(t, u);
return checked_result<T>(e, "addition result");
}
// unary +
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator+(
const checked_result<T> & t
){
return t;
}
// integers subtraction
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator-(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = sum_value_type;
constexpr const std::uint8_t order = static_cast<std::uint8_t>(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>(t, u);
return checked_result<T>(e, "subtraction result");
}
// unary -
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator-(
const checked_result<T> & t
){
// assert(false);
return checked_result<T>(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<class T>
constexpr flag to_flag(const checked_result<T> & t) const {
switch(static_cast<safe_numerics_error>(t)){
case safe_numerics_error::success:
return (t < checked_result<T>(0))
? less_than_zero
: (t > checked_result<T>(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<class T>
constexpr product_value_type(const checked_result<T> & t) :
m_flag(to_flag(t))
{}
constexpr operator std::uint8_t () const {
return static_cast<std::uint8_t>(m_flag);
}
};
// integers multiplication
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator*(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = product_value_type;
const std::uint8_t order = static_cast<std::uint8_t>(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>(t, u);
case value_type::indeterminate:
return safe_numerics_error::range_error;
default:
assert(false);
}
return checked_result<T>(0); // to suppress msvc warning
}
// integers division
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator/(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = product_value_type;
const std::uint8_t order = static_cast<std::uint8_t>(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>(t, u);
case value_type::indeterminate:
return safe_numerics_error::range_error;
default:
assert(false);
}
return checked_result<T>(0); // to suppress msvc warning
}
// integers modulus
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator%(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = product_value_type;
const std::uint8_t order = static_cast<std::uint8_t>(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>(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<T>(u, 1);
case value_type::z_value:
return checked::subtract<T>(1, u);
case value_type::greater_than_max:
case value_type::less_than_min:
default:
assert(false);
}
// suppress msvc warning
return checked_result<T>(0);
}
// comparison operators
template<class T>
constexpr boost::logic::tribool operator<(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = sum_value_type;
constexpr const std::uint8_t order = static_cast<std::uint8_t>(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<const T &>(t) < static_cast<const T &>(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<class T>
constexpr boost::logic::tribool
operator>=(
const checked_result<T> & t,
const checked_result<T> & u
){
return !(t < u);
}
template<class T>
constexpr boost::logic::tribool
operator>(
const checked_result<T> & t,
const checked_result<T> & u
){
return u < t;
}
template<class T>
constexpr boost::logic::tribool
operator<=(
const checked_result<T> & t,
const checked_result<T> & u
){
return !(u < t);
}
template<class T>
constexpr boost::logic::tribool
operator==(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = sum_value_type;
constexpr const std::uint8_t order = static_cast<std::uint8_t>(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<const T &>(t) == static_cast<const T &>(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<class T>
constexpr boost::logic::tribool
operator!=(
const checked_result<T> & t,
const checked_result<T> & u
){
return ! (t == u);
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator>>(
const checked_result<T> & t,
const checked_result<T> & u
);
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator~(
const checked_result<T> & t
){
// assert(false);
return ~t.m_r;
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator<<(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = product_value_type;
const std::uint8_t order = static_cast<std::uint8_t>(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<T>(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<bool>(t < checked_result<T>(0)));
assert(static_cast<bool>(u < checked_result<T>(0)));
return t >> -u;
}
else
if(5 == i){
return t;
}
else
if(6 == i){ // - (-t << u)
assert(static_cast<bool>(t < checked_result<T>(0)));
assert(static_cast<bool>(u > checked_result<T>(0)));
const checked_result<T> temp_t = t * checked_result<T>(2);
const checked_result<T> temp_u = u - checked_result<T>(1);
return - (-temp_t << temp_u);
}
else
if(7 == i){ // t >> -u
assert(static_cast<bool>(t > checked_result<T>(0)));
assert(static_cast<bool>(u < checked_result<T>(0)));
return t >> -u;
}
else
if(8 == i){ // t << u
assert(static_cast<bool>(t > checked_result<T>(0)));
assert(static_cast<bool>(u > checked_result<T>(0)));
checked_result<T> r = checked::left_shift<T>(t, u);
return (r.m_e == safe_numerics_error::shift_too_large)
? checked_result<T>(safe_numerics_error::positive_overflow_error)
: r;
}
else
if(9 == i){
return safe_numerics_error::positive_overflow_error;
}
else{
assert(false);
};
return checked_result<T>(0); // to suppress msvc warning
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator>>(
const checked_result<T> & t,
const checked_result<T> & u
){
using value_type = product_value_type;
const std::uint8_t order = static_cast<std::uint8_t>(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<T>(0);
}
else
if(4 == i){ // - (-t << -u)
assert(static_cast<bool>(t < checked_result<T>(0)));
assert(static_cast<bool>(u < checked_result<T>(0)));
return t << -u;
}
else
if(5 == i){
return t;
}
else
if(6 == i){ // - (-t >> u)
assert(static_cast<bool>(t < checked_result<T>(0)));
assert(static_cast<bool>(u > checked_result<T>(0)));
const checked_result<T> temp_t = t / checked_result<T>(2);
const checked_result<T> temp_u = u - checked_result<T>(1);
return - (-temp_t >> temp_u);
}
else
if(7 == i){ // t << -u,
assert(static_cast<bool>(t > checked_result<T>(0)));
assert(static_cast<bool>(u < checked_result<T>(0)));
return t << -u;
}
else
if(8 == i){ // t >> u
assert(static_cast<bool>(t > checked_result<T>(0)));
assert(static_cast<bool>(u > checked_result<T>(0)));
checked_result<T> r = checked::right_shift<T>(t, u);
return (r.m_e == safe_numerics_error::shift_too_large)
? checked_result<T>(0)
: r;
}
else
if(9 == i){
return safe_numerics_error::positive_overflow_error;
}
else{
assert(false);
};
return checked_result<T>(0); // to suppress msvc warning
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator|(
const checked_result<T> & t,
const checked_result<T> & u
){
return
t.exception() || u.exception()
? checked_result<T>(safe_numerics_error::range_error)
: checked::bitwise_or<T>(
static_cast<T>(t),
static_cast<T>(u)
);
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator^(
const checked_result<T> & t,
const checked_result<T> & u
){
return
t.exception() || u.exception()
? checked_result<T>(safe_numerics_error::range_error)
: checked::bitwise_xor<T>(
static_cast<T>(t),
static_cast<T>(u)
);
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value,
checked_result<T>
>::type
constexpr inline operator&(
const checked_result<T> & t,
const checked_result<T> & u
){
return
t.exception() || u.exception()
? checked_result<T>(safe_numerics_error::range_error)
: checked::bitwise_and<T>(
static_cast<T>(t),
static_cast<T>(u)
);
}
} // safe_numerics
} // boost
#include <iosfwd>
namespace std {
template<typename CharT, typename Traits, typename R>
inline std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const boost::safe_numerics::checked_result<R> & r
){
if(!r.exception())
os << static_cast<R>(r);
else
os << std::error_code(r.m_e).message() << ':' << r.m_msg;
return os;
}
template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const boost::safe_numerics::checked_result<signed char> & r
){
if(! r.exception())
os << static_cast<std::int16_t>(r);
else
os << std::error_code(r.m_e).message() << ':' << r.m_msg;
return os;
}
template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const boost::safe_numerics::checked_result<unsigned char> & r
){
if(! r.exception())
os << static_cast<std::uint16_t>(r);
else
os << std::error_code(r.m_e).message() << ':' << r.m_msg;
return os;
}
template<typename CharT, typename Traits, typename R>
inline std::basic_istream<CharT, Traits> & operator>>(
std::basic_istream<CharT, Traits> & is,
boost::safe_numerics::checked_result<R> & r
){
is >> r.m_r;
return is;
}
template<typename CharT, typename Traits, typename R>
inline std::basic_istream<CharT, Traits> & operator>>(
std::basic_istream<CharT, Traits> & is,
boost::safe_numerics::checked_result<signed char> & r
){
std::int16_t i;
is >> i;
r.m_r = i;
return is;
}
template<typename CharT, typename Traits, typename R>
inline std::basic_istream<CharT, Traits> & operator>>(
std::basic_istream<CharT, Traits> & is,
boost::safe_numerics::checked_result<unsigned char> & r
){
std::uint16_t i;
is >> i;
r.m_r = i;
return is;
}
} // std
/////////////////////////////////////////////////////////////////
// numeric limits for checked<R>
#include <limits>
namespace std {
template<class R>
class numeric_limits<boost::safe_numerics::checked_result<R> >
: public std::numeric_limits<R>
{
using this_type = boost::safe_numerics::checked_result<R>;
public:
constexpr static this_type min() noexcept {
return this_type(std::numeric_limits<R>::min());
}
constexpr static this_type max() noexcept {
return this_type(std::numeric_limits<R>::max());
}
};
} // std
#endif // BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS