Files
safe_numerics/include/automatic.hpp
2017-05-25 11:01:24 -07:00

362 lines
13 KiB
C++

#ifndef BOOST_NUMERIC_AUTOMATIC_HPP
#define BOOST_NUMERIC_AUTOMATIC_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
// 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)
// policy which creates expanded results types designed
// to avoid overflows.
#include <limits>
#include <cstdint> // (u)intmax_t,
#include <type_traits> // true_type, false_type, is_same
#include <boost/mpl/if.hpp>
#include <boost/mpl/min_max.hpp>
#include "safe_common.hpp"
#include "checked_result.hpp"
#include "interval.hpp"
#include "utility.hpp"
namespace boost {
namespace numeric {
struct automatic {
// the following returns the "true" type. After calculating the new max and min
// these return the minimum size type which can hold the expected result.
template<typename T, T Min, T Max>
struct defer_stored_signed_lazily {
using type = utility::signed_stored_type<Min, Max>;
};
template<typename T, T Min, T Max>
struct defer_stored_unsigned_lazily {
using type = utility::unsigned_stored_type<Min, Max>;
};
template<typename T, T Min, T Max>
using result_type =
typename boost::mpl::if_c<
std::numeric_limits<T>::is_signed,
defer_stored_signed_lazily<T, Min, Max>,
defer_stored_unsigned_lazily<T, Min, Max>
>::type;
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct addition_result {
using temp_base_type = typename boost::mpl::if_c<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
constexpr static const interval<typename base_type<T>::type> t_interval{
base_value(std::numeric_limits<T>::min()),
base_value(std::numeric_limits<T>::max())
};
constexpr static const interval<typename base_type<U>::type> u_interval{
base_value(std::numeric_limits<U>::min()),
base_value(std::numeric_limits<U>::max())
};
constexpr static const interval<checked_result<temp_base_type>>
r_interval = add<temp_base_type>(t_interval, u_interval);
using type = typename result_type<
temp_base_type,
r_interval.l.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(r_interval.l),
r_interval.u.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(r_interval.u)
>::type;
//utility::print_type<
// utility::signed_stored_type<-32768, 33022>
//> p1;
//utility::print_value<
// utility::significant_bits(-32768)
// utility::significant_bits(33022)
// utility::signed_stored_type<-32768, 33022>
//> p1;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct subtraction_result {
// result of subtraction are always signed.
using temp_base_type = intmax_t;
constexpr static const interval<typename base_type<T>::type> t_interval{
base_value(std::numeric_limits<T>::min()),
base_value(std::numeric_limits<T>::max())
};
constexpr static const interval<typename base_type<U>::type> u_interval{
base_value(std::numeric_limits<U>::min()),
base_value(std::numeric_limits<U>::max())
};
constexpr static const interval<checked_result<temp_base_type>>
r_interval = subtract<temp_base_type>(t_interval, u_interval);
using type = typename result_type<
temp_base_type,
r_interval.l.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(r_interval.l),
r_interval.u.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(r_interval.u)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct multiplication_result {
using temp_base_type = typename boost::mpl::if_c<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
constexpr static const interval<typename base_type<T>::type> t_interval{
base_value(std::numeric_limits<T>::min()),
base_value(std::numeric_limits<T>::max())
};
constexpr static const interval<typename base_type<U>::type> u_interval{
base_value(std::numeric_limits<U>::min()),
base_value(std::numeric_limits<U>::max())
};
constexpr static const interval<checked_result<temp_base_type>>
r_interval = multiply<temp_base_type>(t_interval, u_interval);
using type = typename result_type<
temp_base_type,
r_interval.l.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(r_interval.l),
r_interval.u.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(r_interval.u)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct division_result {
using temp_base_type = typename boost::mpl::if_c<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
constexpr static const interval<typename base_type<T>::type> t_interval{
base_value(std::numeric_limits<T>::min()),
base_value(std::numeric_limits<T>::max())
};
constexpr static const interval<typename base_type<U>::type> u_interval{
base_value(std::numeric_limits<U>::min()),
base_value(std::numeric_limits<U>::max())
};
constexpr static const interval<checked_result<temp_base_type>>
r_interval = divide<temp_base_type>(t_interval, u_interval);
constexpr static temp_base_type lower_helper =
r_interval.l.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(r_interval.l);
constexpr static temp_base_type lower =
lower_helper < 0 &&
lower_helper != std::numeric_limits<temp_base_type>::min()
? lower_helper - 1
: lower_helper;
constexpr static temp_base_type upper =
r_interval.u.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(r_interval.u);
using type = typename result_type<
temp_base_type,
lower,
upper
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct modulus_result {
using temp_base_type = typename boost::mpl::if_c<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
constexpr static const interval<typename base_type<T>::type> t_interval{
base_value(std::numeric_limits<T>::min()),
base_value(std::numeric_limits<T>::max())
};
constexpr static const interval<typename base_type<U>::type> u_interval{
base_value(std::numeric_limits<U>::min()),
base_value(std::numeric_limits<U>::max())
};
constexpr static const interval<checked_result<temp_base_type>>
r_interval = modulus<temp_base_type>(t_interval, u_interval);
using type = typename result_type<
temp_base_type,
r_interval.l.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(r_interval.l),
r_interval.u.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(r_interval.u)
>::type;
};
///////////////////////////////////////////////////////////////////////
// shift operations
template<typename T, typename U>
struct left_shift_result {
using t_base_type = typename base_type<T>::type;
using u_base_type = typename base_type<U>::type;
constexpr static const interval<t_base_type> t_interval{
base_value(std::numeric_limits<T>::min()),
base_value(std::numeric_limits<T>::max())
};
using temp_base_type = typename boost::mpl::if_c<
std::numeric_limits<T>::is_signed,
std::intmax_t,
std::uintmax_t
>::type;
constexpr static const interval<u_base_type> u_interval{
base_value(std::numeric_limits<U>::min()),
base_value(std::numeric_limits<U>::max())
};
constexpr static const interval<checked_result<temp_base_type>> r_interval =
left_shift<temp_base_type>(t_interval, u_interval);
using type = typename result_type<
temp_base_type,
r_interval.l.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(r_interval.l),
r_interval.u.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(r_interval.u)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct right_shift_result {
using t_base_type = typename base_type<T>::type;
using u_base_type = typename base_type<U>::type;
constexpr static const interval<t_base_type> t_interval{
base_value(std::numeric_limits<T>::min()),
base_value(std::numeric_limits<T>::max())
};
using temp_base_type = typename boost::mpl::if_c<
std::numeric_limits<T>::is_signed,
std::intmax_t,
std::uintmax_t
>::type;
constexpr static const interval<u_base_type> u_interval{
base_value(std::numeric_limits<U>::min()),
base_value(std::numeric_limits<U>::max())
};
constexpr static const interval<checked_result<temp_base_type>> r_interval =
right_shift<temp_base_type>(t_interval, u_interval);
using type = typename result_type<
temp_base_type,
r_interval.l.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(r_interval.l),
r_interval.u.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(r_interval.u)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct bitwise_and_result {
using t_base_type = typename base_type<T>::type;
using u_base_type = typename base_type<U>::type;
using type = typename boost::mpl::if_c<
(sizeof(t_base_type) > sizeof(u_base_type)),
u_base_type,
t_base_type
>::type;
};
template<typename T, typename U>
struct bitwise_or_result {
using t_base_type = typename base_type<T>::type;
using u_base_type = typename base_type<U>::type;
using type = typename boost::mpl::if_c<
(sizeof(t_base_type) > sizeof(u_base_type)),
t_base_type,
u_base_type
>::type;
};
template<typename T, typename U>
struct bitwise_xor_result {
using t_base_type = typename base_type<T>::type;
using u_base_type = typename base_type<U>::type;
using type = typename boost::mpl::if_c<
(sizeof(t_base_type) > sizeof(u_base_type)),
t_base_type,
u_base_type
>::type;
};
};
} // numeric
} // boost
#endif // BOOST_NUMERIC_AUTOMATIC_HPP