mirror of
https://github.com/yuzu-emu/ext-boost.git
synced 2024-12-23 03:35:27 +00:00
1218 lines
42 KiB
C++
1218 lines
42 KiB
C++
#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
|