#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 results types equal to that of C++ promotions. // Using the policy will permit the program to build and run in release // mode which is identical to that in debug mode except for the fact // that errors aren't trapped. #include #include // (u)intmax_t, #include // true_type, false_type, is_same #include #include "utility.hpp" #include "safe_common.hpp" #include "checked_result.hpp" #include "interval.hpp" namespace boost { namespace numeric { struct automatic { // section 4.13 integer conversion rank template using rank = typename boost::mpl::if_c< sizeof(char) == sizeof(T), std::integral_constant, typename boost::mpl::if_c< sizeof(short) == sizeof(T), std::integral_constant, typename boost::mpl::if_c< sizeof(int) == sizeof(T), std::integral_constant, typename boost::mpl::if_c< sizeof(long) == sizeof(T), std::integral_constant, typename boost::mpl::if_c< sizeof(long long) == sizeof(T), std::integral_constant, void >::type >::type >::type >::type >::type; // note presumption that T & U don't have he same sign // if that's not true, these won't work template using select_signed = typename boost::mpl::if_c< std::numeric_limits::is_signed, T, U >::type; template using select_unsigned = typename boost::mpl::if_c< std::numeric_limits::is_signed, U, T >::type; template using calculate_max_t = typename boost::mpl::if_c< // clause 1 - if both operands have the same sign std::numeric_limits::is_signed == std::numeric_limits::is_signed, // use that sign typename boost::mpl::if_c< std::numeric_limits::is_signed, std::intmax_t, std::uintmax_t >::type, // clause 2 - otherwise if the rank of the unsigned type exceeds // the rank of the of the maximum signed type typename boost::mpl::if_c< (rank< select_unsigned>::value > rank< std::intmax_t >::value), // use unsigned type std::uintmax_t, // clause 3 - otherwise if the type of the signed integer type can // represent all the values of the unsigned type typename boost::mpl::if_c< std::numeric_limits< std::intmax_t >::digits >= std::numeric_limits< select_unsigned >::digits, // use signed type std::intmax_t, // clause 4 - otherwise use unsigned version of the signed type std::uintmax_t >::type >::type >::type; template struct defer_stored_signed_lazily { using type = signed_stored_type; }; template struct defer_stored_unsigned_lazily { using type = unsigned_stored_type; }; template using result_type = typename boost::mpl::if_< std::is_signed, defer_stored_signed_lazily, defer_stored_unsigned_lazily >::type; /////////////////////////////////////////////////////////////////////// template struct addition_result { using result_base_type = calculate_max_t; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; // filter out case were overflow cannot occur // note: subtle trickery. Suppose t is safe_range. Then // std::numeric_limits::min() will be safe_range t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; // when we add the temporary intervals above, we'll get a new interval // with the correct range for the sum ! constexpr static const checked_result> r_interval = add(t_interval, u_interval); constexpr static const interval result_interval = r_interval.no_exception() ? static_cast>(r_interval) : interval{} ; using type = typename result_type< result_base_type, result_interval.l, result_interval.u >::type; }; /////////////////////////////////////////////////////////////////////// template struct subtraction_result { // subtraction can result in negative result regardless of the // operand types ! using result_base_type = std::intmax_t; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; constexpr static const checked_result> r_interval = subtract(t_interval, u_interval); constexpr static const interval result_interval = r_interval.no_exception() ? static_cast>(r_interval) : interval{} ; using type = typename result_type< result_base_type, result_interval.l, result_interval.u >::type; }; /////////////////////////////////////////////////////////////////////// template struct multiplication_result { using result_base_type = calculate_max_t; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; constexpr static const checked_result> r_interval = multiply(t_interval, u_interval); constexpr static const interval result_interval = r_interval.no_exception() ? static_cast>(r_interval) : interval{} ; using type = typename result_type< result_base_type, result_interval.l, result_interval.u >::type; }; /////////////////////////////////////////////////////////////////////// template struct division_result { using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; using result_base_type = typename boost::mpl::if_c< std::numeric_limits::is_signed || std::numeric_limits::is_signed, std::intmax_t, std::uintmax_t >::type; constexpr static checked_result> r { divide_nz(t_interval, u_interval) }; constexpr static const interval result_interval { r.no_exception() ? static_cast>(r) : interval{} }; using type = typename result_type< result_base_type, result_interval.l, result_interval.u >::type; }; // forward to correct divide implementation template checked_result static constexpr divide( const T & t, const U & u ){ return checked::divide_automatic(t, u); } /////////////////////////////////////////////////////////////////////// template struct modulus_result { using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; constexpr static const interval u_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; using r_base_type = std::make_unsigned_t; constexpr static const checked_result> r { modulus_nz(t_interval, u_interval) }; constexpr static const interval result_interval = r.no_exception() ? static_cast>(r) : interval{} ; using type = typename result_type< r_base_type, result_interval.l, result_interval.u >::type; }; // forward to correct modulus implementation template checked_result static constexpr modulus( const T & t, const U & u ){ return checked::modulus(t, u); } /////////////////////////////////////////////////////////////////////// template struct left_shift_result { using t_base_type = typename base_type::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; using type = typename result_type< T, t_interval.l, t_interval.u >::type; }; /////////////////////////////////////////////////////////////////////// template struct right_shift_result { using t_base_type = typename base_type::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), base_value(std::numeric_limits::max()) }; using type = typename result_type< T, t_interval.l, t_interval.u >::type; }; /////////////////////////////////////////////////////////////////////// template struct bitwise_result { using t_base_type = typename base_type::type; using u_base_type = typename base_type::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