Implemented divide operations

updated test_add
This commit is contained in:
Robert Ramey
2015-06-15 16:18:30 -07:00
parent 6c0365461b
commit 9ba1617c4e
9 changed files with 367 additions and 249 deletions

View File

@@ -381,7 +381,7 @@ namespace checked {
const T & t,
const U & u
) {
static_assert(! is_safe<T>::value, "should only be a base type here!");
static_assert(! is_safe<T>::value, "should not be a base type here!");
return
detail::cast<R>(t) != checked_result<R>::exception_type::no_exception ?
detail::cast<R>(t)
@@ -410,134 +410,88 @@ namespace checked {
;
}
/*
////////////////////////////////
// safe division on unsafe types
namespace detail {
////////////////////////////////
// multiplication implementation
template<class T, class U>
decltype(T() * U())
check_multiplication_overflow(const T & t, const U & u){
typedef decltype(T() * U()) result_type;
char const * const msg = "safe range multiplication overflow";
// presume that size of uintmax_t and intmax_t are the same
typedef bits<boost::uintmax_t> available_bits;
if(multiply_result_bits<T, U>::value
<= boost::numeric::bits<result_type>::value)
return t * u;
if(multiply_result_bits<T, U>::value <= available_bits::value){
typedef typename multiply_result_type<T, U>::type temp_type;
temp_type tmp = static_cast<temp_type>(t) * temp_type(u);
// the following works for both positive and negative results
// and for both signed and unsigned numbers
if(tmp > boost::integer_traits<result_type>::const_max)
boost::numeric::overflow(msg);
if(tmp < boost::integer_traits<result_type>::const_min)
boost::numeric::overflow(msg);
return static_cast<result_type>(tmp);
}
// when the there is no native type which can hold the product
// use multible precision
// t is factored as (a << temp_bits) + b
// u is factored as (c << temp_bits) + d
// so we use multi-precision:
// a + b
// c + d
// -----
// bd
// ad
// cb
// ac
// -----
// ..
if(boost::numeric::is_unsigned<result_type>::value
&& (t < 0 || u < 0))
overflow("conversion of negative value to unsigned");
if(t == 1)
return u;
if(u == 1)
return t;
result_type rt = t;
if(rt < 0){
rt = ~rt + 1;
// address
if(rt < 0)
overflow("overflow of negative value");
}
result_type ru = u;
if(ru < 0){
ru = ~ru + 1;
// address
if(ru < 0)
overflow("overflow of negative value");
}
// check positive values for overflow
// t is factored as (a << temp_bits) + b
// u is factored as (c << temp_bits) + d
// so we use multi-precision:
// a + b
// c + d
// -----
// bd
// ad
// cb
// ac
// -----
// ..
typedef boost::uintmax_t accumulator_type;
const int temp_bits = bits<accumulator_type>::value / 2;
typedef typename boost::uint_t<temp_bits>::least temp_type;
temp_type a = (static_cast<accumulator_type>(rt) >> temp_bits);
temp_type c = (static_cast<accumulator_type>(ru) >> temp_bits);
if(0 != a && 0 != c)
overflow(msg);
temp_type b = static_cast<temp_type>(rt);
if((static_cast<accumulator_type>(b) * static_cast<accumulator_type>(c) >> temp_bits) > 0)
overflow(msg);
temp_type d = static_cast<const temp_type>(ru);
if(0 != (static_cast<accumulator_type>(a) * static_cast<accumulator_type>(d) >> temp_bits))
overflow(msg);
return t * u;
template<class R>
typename boost::enable_if_c<
std::is_unsigned<R>::value,
checked_result<R>
>::type
SAFE_NUMERIC_CONSTEXPR divide(
const R & minr,
const R & maxr,
const R t,
const R u
) {
return checked_result<R>(t / u);
}
template<class T, class U>
decltype(T() / U())
check_division_overflow(const T & t, const U & u){
if(0 == u)
overflow("divide by zero");
if(boost::numeric::is_signed<U>::value){
// t unsigned, u signed
if(boost::numeric::is_unsigned<T>::value){
if(u < 0){
overflow("conversion of negative value to unsigned");
}
}
else{
// both signed
// pathological case: change sign on negative number so it overflows
if(t == boost::integer_traits<T>::const_min && u == -1)
overflow("overflow in result");
}
}
// both unsigned
// t signed, u unsigned
return t / u;
template<class R>
typename boost::enable_if_c<
std::is_signed<R>::value,
checked_result<R>
>::type
SAFE_NUMERIC_CONSTEXPR divide(
const R & minr,
const R & maxr,
const R t,
const R u
){
return
// note presumption of two's complement arithmetic
(u < 0 && t == std::numeric_limits<R>::min()) ?
checked_result<R>(
checked_result<R>::exception_type::domain_error,
"divide by zero"
)
:
checked_result<R>(t / u)
;
}
} // namespace detail
template<class R, class T, class U>
SAFE_NUMERIC_CONSTEXPR checked_result<R> divide(
const R & minr,
const R & maxr,
const T & t,
const U & u
) {
static_assert(! is_safe<T>::value, "should not be a base type here!");
return
detail::cast<R>(t) != checked_result<R>::exception_type::no_exception ?
detail::cast<R>(t)
:
detail::cast<R>(u) != checked_result<R>::exception_type::no_exception ?
detail::cast<R>(u)
:
u == 0 ?
checked_result<R>(
checked_result<R>::exception_type::domain_error,
"divide by zero"
)
:
detail::divide<R>(minr, maxr, t, u)
;
}
template<class R, class T, class U>
SAFE_NUMERIC_CONSTEXPR checked_result<R> divide(
const T & t,
const U & u
) {
return
divide<R, T, U>(
std::numeric_limits<R>::min(),
std::numeric_limits<R>::max(),
t,
u
)
;
}
/*
template<class T, class U>
decltype(T() / U())
check_modulus_overflow(const T & t, const U & u){
@@ -562,8 +516,7 @@ namespace checked {
// t signed, u unsigned
return t % u;
}
} // detail
*/
*/
} // checked
} // numeric
} // boost

View File

@@ -23,7 +23,8 @@ struct checked_result {
no_exception,
overflow_error,
underflow_error,
range_error
range_error,
domain_error
};
exception_type m_e;
union {
@@ -103,6 +104,9 @@ struct checked_result {
case checked_result<R>::exception_type::range_error:
EP::range_error(m_msg);
break;
case checked_result<R>::exception_type::domain_error:
EP::domain_error(m_msg);
break;
case checked_result<R>::exception_type::no_exception:
break;
default:
@@ -112,7 +116,7 @@ struct checked_result {
};
template<typename R>
SAFE_NUMERIC_CONSTEXPR inline const checked_result<R> minxx(const checked_result<R> & t, const checked_result<R> & u){
SAFE_NUMERIC_CONSTEXPR inline const checked_result<R> min(const checked_result<R> & t, const checked_result<R> & u){
return
(t.m_e == checked_result<R>::exception_type::no_exception
&& u.m_e == checked_result<R>::exception_type::no_exception) ?
@@ -127,8 +131,9 @@ SAFE_NUMERIC_CONSTEXPR inline const checked_result<R> minxx(const checked_result
)
;
}
template<typename R>
SAFE_NUMERIC_CONSTEXPR inline const checked_result<R> maxxx(const checked_result<R> & t, const checked_result<R> & u){
SAFE_NUMERIC_CONSTEXPR inline const checked_result<R> max(const checked_result<R> & t, const checked_result<R> & u){
return
(t.m_e == checked_result<R>::exception_type::no_exception
&& u.m_e == checked_result<R>::exception_type::no_exception) ?

View File

@@ -24,6 +24,7 @@ struct ExceptionPolicy {
EP::overflow_error(message);
EP::underflow_error(message);
EP::range_error(message);
EP::domain_error(message);
}
};

View File

@@ -31,6 +31,7 @@ struct ignore_exception {
static void overflow_error(const char * message) {}
static void underflow_error(const char * message) {}
static void range_error(const char * message) {}
static void domain_error(const char * message) {}
};
// If an exceptional condition is detected at runtime throw the exception.
@@ -45,6 +46,9 @@ struct throw_exception {
static void range_error(const char * message) {
throw std::domain_error(message);
}
static void domain_error(const char * message) {
throw std::domain_error(message);
}
};
// example - if you want to specify specific behavior for particular exception
@@ -54,7 +58,8 @@ struct throw_exception {
template<
void (*OVERFLOW)(const char *),
void (*UNDERFLOW)(const char *),
void (*RANGE)(const char *)
void (*RANGE)(const char *),
void (*DOMAIN)(const char *)
>
struct no_exception_support {
BOOST_CONCEPT_ASSERT((boost::numeric::ExceptionPolicy<no_exception_support>));
@@ -67,6 +72,9 @@ struct no_exception_support {
static void range_error(const char * message) {
RANGE(message);
}
static void domain_error(const char * message) {
DOMAIN(message);
}
};
// use this policy to trap at compile time any operation which
@@ -90,6 +98,10 @@ struct trap_exception {
static void range_error(const T *) {
static_assert(std::is_void<T>::value, "range_error");
}
template<class T>
static void domain_error(const T *) {
static_assert(std::is_void<T>::value, "domain_error");
}
};
} // namespace numeric

View File

@@ -54,11 +54,11 @@ SAFE_NUMERIC_CONSTEXPR interval<R> operator*(const interval<T> & t, const interv
(u.l < 0) ?
(u.u > 0) ? // M * M
interval<R>(
boost::numeric::minxx(
min(
checked::multiply<R>(t.l, u.u),
checked::multiply<R>(t.u, u.l)
),
boost::numeric::maxxx(
max(
checked::multiply<R>(t.l, u.u),
checked::multiply<R>(t.u, u.l)
)
@@ -119,6 +119,102 @@ SAFE_NUMERIC_CONSTEXPR interval<R> operator*(const interval<T> & t, const interv
;
}
template<typename R, typename T, typename U>
SAFE_NUMERIC_CONSTEXPR interval<R> operator/(const interval<T> & t, const interval<U> & u){
// adapted from https://en.wikipedia.org/wiki/Interval_arithmetic
return
(u.l <= 0) ?
interval<R>(
0,
checked_result<R>(
checked_result<R>::exception_type::domain_error,
"interval divisor includes zero"
)
)
:
interval<R>(
min(
min(
checked::divide<R>(t.l, u.l),
checked::divide<R>(t.l, u.u)
),
min(
checked::divide<R>(t.u, u.l),
checked::divide<R>(t.u, u.u)
)
),
max(
max(
checked::divide<R>(t.l, u.l),
checked::divide<R>(t.l, u.u)
),
max(
checked::divide<R>(t.u, u.l),
checked::divide<R>(t.u, u.u)
)
)
)
;
}
/*
template<typename R, typename T, typename U>
SAFE_NUMERIC_CONSTEXPR interval<R> operator/(const interval<T> & t, const interval<U> & u){
// adapted from boost interval library
return
(t.u < 0) ?
(u.u < 0) ?
interval<R>(
checked_result<R>(t.u, u.l),
checked_result<R>(t.l, u.u)
)
:
interval<R>(
checked_result<R>(t.l, u.l),
checked_result<R>(t.u, u.u)
)
:
(t.l < 0) ?
(u.u < 0) ?
interval<R>(
checked_result<R>(t.u, u.u),
checked_result<R>(t.l, u.u)
)
:
interval<R>(
checked_result<R>(t.l, u.l),
checked_result<R>(t.u, u.l)
)
:
(u.u < 0) ?
interval<R>(
checked_result<R>(t.u, u.u),
checked_result<R>(t.l, u.l)
)
:
interval<R>(
checked_result<R>(t.l, u.u),
checked_result<R>(t.u, u.l)
)
;
}
if (::boost::numeric::interval_lib::user::is_neg(xu))
if (::boost::numeric::interval_lib::user::is_neg(yu))
return I(rnd.div_down(xu, yl), rnd.div_up(xl, yu), true);
else
return I(rnd.div_down(xl, yl), rnd.div_up(xu, yu), true);
else if (::boost::numeric::interval_lib::user::is_neg(xl))
if (::boost::numeric::interval_lib::user::is_neg(yu))
return I(rnd.div_down(xu, yu), rnd.div_up(xl, yu), true);
else
return I(rnd.div_down(xl, yl), rnd.div_up(xu, yl), true);
else
if (::boost::numeric::interval_lib::user::is_neg(yu))
return I(rnd.div_down(xu, yu), rnd.div_up(xl, yl), true);
else
return I(rnd.div_down(xl, yu), rnd.div_up(xu, yl), true);
*/
} // numeric
} // boost

View File

@@ -30,42 +30,34 @@ template<
struct safe;
struct native {
template<
typename T,
typename U,
typename P,
typename E
>
template<typename T, typename U, typename P, typename E>
struct addition_result {
typedef typename base_type<T>::type base_type_t;
typedef typename base_type<U>::type base_type_u;
typedef decltype(base_type_t() + base_type_u()) result_base_type;
typedef safe<result_base_type, P, E> type;
};
template<
typename T,
typename U,
typename P,
typename E
>
template<typename T, typename U, typename P, typename E>
struct subtraction_result {
typedef typename base_type<T>::type base_type_t;
typedef typename base_type<U>::type base_type_u;
typedef decltype(base_type_t() - base_type_u()) result_base_type;
typedef safe<result_base_type, P, E> type;
};
template<
typename T,
typename U,
typename P,
typename E
>
template<typename T, typename U, typename P, typename E>
struct multiplication_result {
typedef typename base_type<T>::type base_type_t;
typedef typename base_type<U>::type base_type_u;
typedef decltype(base_type_t() * base_type_u()) result_base_type;
typedef safe<result_base_type, P, E> type;
};
template<typename T, typename U, typename P, typename E>
struct division_result {
typedef typename base_type<T>::type base_type_t;
typedef typename base_type<U>::type base_type_u;
typedef decltype(base_type_t() / base_type_u()) result_base_type;
typedef safe<result_base_type, P, E> type;
};
};
} // numeric

View File

@@ -310,22 +310,74 @@ inline operator*(const T & t, const U & u){
return static_cast<result_type>(r);
}
/*
/////////////////////////////////////////////////////////////////
// division
// special case - possible overflow
template<class T, class Stored, class Derived, class Policies>
typename boost::enable_if<
std::is_integral<T>,
decltype(T() / Stored())
template<class T, class U>
struct division_result {
typedef common_policies<T, U> P;
typedef typename P::promotion_policy::template division_result<
T,
U,
typename P::promotion_policy,
typename P::exception_policy
>::type type;
};
template<class T, class U>
typename boost::lazy_enable_if<
boost::mpl::or_<
boost::numeric::is_safe<T>,
boost::numeric::is_safe<U>
>,
division_result<T, U>
>::type
inline operator/(const T & lhs, const safe_base<Stored, Derived, Policies> & rhs) {
if(safe_compare::equal(0, rhs))
throw std::domain_error("Divide by zero");
return static_cast<
decltype(T() / Stored())
>(lhs / static_cast<const Stored &>(rhs));
inline operator/(const T & t, const U & u){
// argument dependent lookup should guarentee that we only get here
// only if one of the types is a safe type. Verify this here
typedef division_result<T, U> ar;
typedef typename ar::type result_type;
static_assert(
boost::numeric::is_safe<result_type>::value,
"Promotion failed to return safe type"
);
typedef typename base_type<result_type>::type result_base_type;
typedef typename base_type<T>::type t_base_type;
typedef typename base_type<U>::type u_base_type;
// filter out case were overflow cannot occur
SAFE_NUMERIC_CONSTEXPR const interval<t_base_type> t_interval = {
base_value(std::numeric_limits<t_base_type>::min()),
base_value(std::numeric_limits<t_base_type>::max())
};
SAFE_NUMERIC_CONSTEXPR const interval<u_base_type> u_interval = {
base_value(std::numeric_limits<u_base_type>::min()),
base_value(std::numeric_limits<u_base_type>::max())
};
SAFE_NUMERIC_CONSTEXPR const interval<result_base_type> r_interval
= operator/<result_base_type>(t_interval, u_interval);
// if no over/under flow possible
if(r_interval.no_exception())
return result_type(base_value(t) / base_value(u));
// otherwise do the multiplication checking for overflow
checked_result<result_base_type> r = checked::divide(
base_value(std::numeric_limits<result_type>::min()),
base_value(std::numeric_limits<result_type>::max()),
base_value(t),
base_value(u)
);
r.template dispatch<typename ar::P::exception_policy>();
return static_cast<result_type>(r);
}
/*
// comparison operators
template<class T, class Stored, class Derived, class Policies>
typename boost::enable_if<

View File

@@ -27,10 +27,12 @@ bool test_add(
try{
result = t1 + v2;
static_assert(
boost::numeric::is_safe<decltype(t1 + v2)>::value,
"Expression failed to return safe type"
);
if(expected_result == 'x'){
std::cout
<< "failed to detect error in addition "
@@ -66,14 +68,6 @@ bool test_add(
// presuming native policy
boost::numeric::safe<decltype(v1 + v2)> result;
static_assert(
std::is_same<
boost::numeric::safe<decltype(v1 + v2)>,
decltype(t1 + t2)
>::value,
"unexpected result type"
);
try{
result = t1 + t2;
@@ -81,6 +75,7 @@ bool test_add(
boost::numeric::is_safe<decltype(t1 + t2)>::value,
"Expression failed to return safe type"
);
if(expected_result == 'x'){
std::cout
<< "failed to detect error in addition "

View File

@@ -27,74 +27,86 @@ bool test_divide(
<< "testing "
<< av1 << " / " << av2
<< std::endl;
{
boost::numeric::safe<T1> t1 = v1;
// presuming native policy
boost::numeric::safe<decltype(v1 / v2)> result;
boost::numeric::safe<T1> t1 = v1;
BOOST_TYPEOF_TPL(T1() / T2()) result;
try{
result = t1 / v2;
if(expected_result != '.'){
//if(expected_result == 'x'){
std::cout
<< "failed to detect error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " ! = "<< av1 << " / " << av2
<< std::endl;
try{
result = t1 / v2;
try{
result = t1 / v2;
static_assert(
boost::numeric::is_safe<decltype(t1 + v2)>::value,
"Expression failed to return safe type"
);
if(expected_result == 'x'){
std::cout
<< "failed to detect error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " ! = "<< av1 << " / " << av2
<< std::endl;
try{
t1 / v2;
}
catch(...){}
return false;
}
}
catch(std::exception & e){
if(expected_result == '.'){
std::cout
<< "erroneously detected error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " == "<< av1 << " / " << av2
<< std::endl;
try{
t1 / v2;
}
catch(...){}
return false;
}
catch(...){}
return false;
}
}
catch(std::range_error){
if(expected_result != 'x'){
//if(expected_result == '.'){
std::cout
<< "erroneously detected error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " == "<< av1 << " / " << av2
<< std::endl;
try{
result = t1 / v2;
}
catch(...){}
return false;
}
}
boost::numeric::safe<T2> t2 = v2;
{
boost::numeric::safe<T1> t1 = v1;
boost::numeric::safe<T2> t2 = v2;
try{
result = t1 / t2;
// presuming native policy
boost::numeric::safe<decltype(v1 + v2)> result;
if(expected_result != '.'){
//if(expected_result == 'x'){
std::cout
<< "failed to detect error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " ! = "<< av1 << " / " << av2
<< std::endl;
try{
result = t1 / t2;
try{
result = t1 / t2;
static_assert(
boost::numeric::is_safe<decltype(t1 + t2)>::value,
"Expression failed to return safe type"
);
if(expected_result == 'x'){
std::cout
<< "failed to detect error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " ! = "<< av1 << " / " << av2
<< std::endl;
try{
t1 / t2;
}
catch(...){}
return false;
}
catch(...){}
return false;
}
}
catch(std::range_error){
if(expected_result != 'x'){
//if(expected_result == '.'){
std::cout
<< "erroneously detected error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " == "<< av1 << " / " << av2
<< std::endl;
try{
result = t1 / t2;
catch(std::exception & e){
if(expected_result == '.'){
std::cout
<< "erroneously detected error in division "
<< std::hex << result << "(" << std::dec << result << ")"
<< " == "<< av1 << " / " << av2
<< std::endl;
try{
t1 / t2;
}
catch(...){}
return false;
}
catch(...){}
return false;
}
}
return true;
@@ -113,38 +125,38 @@ const char *test_division_result[VALUE_ARRAY_SIZE] = {
// 01234567890123456789012345678901
/* 0*/ "................................",
/* 1*/ "................................",
/* 2*/ "...x...x...x...x................",
/* 3*/ "................................",
/* 2*/ "........................xxxxxxxx",
/* 3*/ "........................xxxxxxxx",
/* 4*/ ".................................",
/* 5*/ "................................",
/* 6*/ "...x...x...x...x................",
/* 7*/ "................................",
/* 6*/ "........................xxxxxxxx",
/* 7*/ "........................xxxxxxxx",
/* 8*/ "................................",
/* 9*/ "................................",
/*10*/ "...x...x...x...x................",
/*11*/ "................................",
/*10*/ "..xx..xx..xx............xxxxxxxx",
/*11*/ "........................xxxxxxxx",
/*12*/ "................................",
/*13*/ "................................",
/*14*/ "...x...x...x...x................",
/*15*/ "................................",
/*14*/ "..xx..xx..xx..xx............xxxx",
/*15*/ "............................xxxx",
// 0 0 0 0
// 01234567012345670123456701234567
// 01234567890123456789012345678901
/*16*/ "..xx..xx..xx..xx................",
/*17*/ "..xx..xx..xx..xx................",
/*18*/ "..xx..xx..xx..xx................",
/*19*/ "..xx..xx..xx..xx................",
/*20*/ "..xx..xx..xx..xx................",
/*21*/ "..xx..xx..xx..xx................",
/*22*/ "..xx..xx..xx..xx................",
/*23*/ "..xx..xx..xx..xx................",
/*16*/ "................................",
/*17*/ "................................",
/*18*/ "................................",
/*19*/ "................................",
/*20*/ "................................",
/*21*/ "................................",
/*22*/ "................................",
/*23*/ "................................",
/*24*/ "..xx..xx..xx..xx................",
/*25*/ "..xx..xx..xx..xx................",
/*26*/ "..xx..xx..xx..xx................",
/*27*/ "..xx..xx..xx..xx................",
/*24*/ "..xx..xx..xx....................",
/*25*/ "..xx..xx..xx....................",
/*26*/ "..xx..xx..xx....................",
/*27*/ "..xx..xx..xx....................",
/*28*/ "..xx..xx..xx..xx................",
/*29*/ "..xx..xx..xx..xx................",
/*30*/ "..xx..xx..xx..xx................",