/////////////////////////////////////////////////////////////////////////////// // Copyright 2018 John Maddock. 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) #ifndef BOOST_MP_DETAIL_PRECISION_HPP #define BOOST_MP_DETAIL_PRECISION_HPP #include <boost/multiprecision/traits/is_variable_precision.hpp> #include <boost/multiprecision/detail/number_base.hpp> #include <boost/multiprecision/detail/digits.hpp> #include <boost/multiprecision/detail/assert.hpp> namespace boost { namespace multiprecision { namespace detail { template <class B, boost::multiprecision::expression_template_option ET> inline constexpr unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>&, const std::integral_constant<int, 0>&) { return std::numeric_limits<boost::multiprecision::number<B, ET> >::digits10; } template <class B, boost::multiprecision::expression_template_option ET> inline BOOST_MP_CXX14_CONSTEXPR unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>& val, const std::integral_constant<int, 1>&) { // // We have an arbitrary precision integer, take it's "precision" as the // location of the most-significant-bit less the location of the // least-significant-bit, ie the number of bits required to represent the // the value assuming we will have an exponent to shift things by: // return static_cast<unsigned>(val.is_zero() ? 1 : 1 + digits2_2_10(msb(abs(val)) - lsb(abs(val)) + 1)); } template <class B, boost::multiprecision::expression_template_option ET> inline BOOST_MP_CXX14_CONSTEXPR unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>& val, const std::integral_constant<int, 2>&) { // // We have an arbitrary precision rational, take it's "precision" as the // the larger of the "precision" of numerator and denominator: // return (std::max)(current_precision_of_last_chance_imp(numerator(val), std::integral_constant<int, 1>()), current_precision_of_last_chance_imp(denominator(val), std::integral_constant<int, 1>())); } template <class B, boost::multiprecision::expression_template_option ET> inline BOOST_MP_CXX14_CONSTEXPR unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const std::integral_constant<bool, true>&) { return n.precision(); } template <class B, boost::multiprecision::expression_template_option ET> inline constexpr unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& val, const std::integral_constant<bool, false>&) { using tag = std::integral_constant<int, std::numeric_limits<boost::multiprecision::number<B, ET> >::is_specialized && std::numeric_limits<boost::multiprecision::number<B, ET> >::is_integer && std::numeric_limits<boost::multiprecision::number<B, ET> >::is_exact && !std::numeric_limits<boost::multiprecision::number<B, ET> >::is_modulo ? 1 : boost::multiprecision::number_category<boost::multiprecision::number<B, ET> >::value == boost::multiprecision::number_kind_rational ? 2 : 0>; return current_precision_of_last_chance_imp(val, tag()); } template <class R, class Terminal> inline constexpr unsigned current_precision_of_terminal(const Terminal&) { return (R::thread_default_variable_precision_options() >= variable_precision_options::preserve_all_precision) ? (std::numeric_limits<Terminal>::min_exponent ? std::numeric_limits<Terminal>::digits10 : 1 + std::numeric_limits<Terminal>::digits10) : 0; } template <class R, class Terminal> inline constexpr unsigned current_precision_of(const Terminal& r) { return current_precision_of_terminal<R>(R::canonical_value(r)); } template <class R> inline constexpr unsigned current_precision_of(const float&) { using list = typename R::backend_type::float_types; using first_float = typename std::tuple_element<0, list>::type; return (R::thread_default_variable_precision_options() >= variable_precision_options::preserve_all_precision) ? std::numeric_limits<first_float>::digits10 : 0; } template <class R, class Terminal, std::size_t N> inline constexpr unsigned current_precision_of(const Terminal (&)[N]) { // For string literals: return 0; } template <class R, class B, boost::multiprecision::expression_template_option ET> inline constexpr unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const std::true_type&) { return std::is_same<R, boost::multiprecision::number<B, ET> >::value || (std::is_same<typename R::value_type, boost::multiprecision::number<B, ET> >::value && (R::thread_default_variable_precision_options() >= variable_precision_options::preserve_component_precision)) || (R::thread_default_variable_precision_options() >= variable_precision_options::preserve_all_precision) ? current_precision_of_imp(n, boost::multiprecision::detail::is_variable_precision<boost::multiprecision::number<B, ET> >()) : 0; } template <class R, class B, boost::multiprecision::expression_template_option ET> inline constexpr unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const std::false_type&) { return std::is_same<R, boost::multiprecision::number<B, ET> >::value || std::is_same<typename R::value_type, boost::multiprecision::number<B, ET> >::value ? current_precision_of_imp(n, boost::multiprecision::detail::is_variable_precision<boost::multiprecision::number<B, ET> >()) : 0; } template <class R, class B, boost::multiprecision::expression_template_option ET> inline constexpr unsigned current_precision_of(const boost::multiprecision::number<B, ET>& n) { return current_precision_of_imp<R>(n, boost::multiprecision::detail::is_variable_precision<R>()); } template <class R, class tag, class Arg1> inline constexpr unsigned current_precision_of(const expression<tag, Arg1, void, void, void>& expr) { return current_precision_of<R>(expr.left_ref()); } template <class R, class Arg1> inline constexpr unsigned current_precision_of(const expression<terminal, Arg1, void, void, void>& expr) { return current_precision_of<R>(expr.value()); } template <class R, class tag, class Arg1, class Arg2> inline constexpr unsigned current_precision_of(const expression<tag, Arg1, Arg2, void, void>& expr) { return (std::max)(current_precision_of<R>(expr.left_ref()), current_precision_of<R>(expr.right_ref())); } template <class R, class tag, class Arg1, class Arg2, class Arg3> inline constexpr unsigned current_precision_of(const expression<tag, Arg1, Arg2, Arg3, void>& expr) { return (std::max)((std::max)(current_precision_of<R>(expr.left_ref()), current_precision_of<R>(expr.right_ref())), current_precision_of<R>(expr.middle_ref())); } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable : 4130) #endif template <class R, bool = boost::multiprecision::detail::is_variable_precision<R>::value> struct scoped_default_precision { template <class T> constexpr scoped_default_precision(const T&) {} template <class T, class U> constexpr scoped_default_precision(const T&, const U&) {} template <class T, class U, class V> constexpr scoped_default_precision(const T&, const U&, const V&) {} // // This function is never called: in C++17 it won't be compiled either: // unsigned precision() const { BOOST_MP_ASSERT("This function should never be called!!" == nullptr); return 0; } }; #ifdef BOOST_MSVC #pragma warning(pop) #endif template <class R> struct scoped_default_precision<R, true> { template <class T> BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a) { init(has_uniform_precision() ? R::thread_default_precision() : (std::max)(R::thread_default_precision(), current_precision_of<R>(a))); } template <class T, class U> BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b) { init(has_uniform_precision() ? R::thread_default_precision() : (std::max)(R::thread_default_precision(), (std::max)(current_precision_of<R>(a), current_precision_of<R>(b)))); } template <class T, class U, class V> BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b, const V& c) { init(has_uniform_precision() ? R::thread_default_precision() : (std::max)((std::max)(current_precision_of<R>(a), current_precision_of<R>(b)), (std::max)(R::thread_default_precision(), current_precision_of<R>(c)))); } ~scoped_default_precision() { if(m_new_prec != m_old_prec) R::thread_default_precision(m_old_prec); } BOOST_MP_CXX14_CONSTEXPR unsigned precision() const { return m_new_prec; } static constexpr bool has_uniform_precision() { return R::thread_default_variable_precision_options() <= boost::multiprecision::variable_precision_options::assume_uniform_precision; } private: BOOST_MP_CXX14_CONSTEXPR void init(unsigned p) { m_old_prec = R::thread_default_precision(); if (p && (p != m_old_prec)) { R::thread_default_precision(p); m_new_prec = p; } else m_new_prec = m_old_prec; } unsigned m_old_prec, m_new_prec; }; template <class T> inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T*, const std::integral_constant<bool, false>&) {} template <class T> inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T* obj, const std::integral_constant<bool, true>&) { if (obj->precision() != T::thread_default_precision()) { obj->precision(T::thread_default_precision()); } } template <class T> inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T* obj) { maybe_promote_precision(obj, std::integral_constant<bool, boost::multiprecision::detail::is_variable_precision<T>::value>()); } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR #define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) \ if \ constexpr(boost::multiprecision::detail::is_variable_precision<T>::value) #else #define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) if (boost::multiprecision::detail::is_variable_precision<T>::value) #endif template <class T, bool = boost::multiprecision::detail::is_variable_precision<T>::value> struct scoped_target_precision { variable_precision_options opts; scoped_target_precision() : opts(T::thread_default_variable_precision_options()) { T::thread_default_variable_precision_options(variable_precision_options::preserve_target_precision); } ~scoped_target_precision() { T::thread_default_variable_precision_options(opts); } }; template <class T> struct scoped_target_precision<T, false> {}; template <class T, bool = boost::multiprecision::detail::is_variable_precision<T>::value> struct scoped_source_precision { variable_precision_options opts; scoped_source_precision() : opts(T::thread_default_variable_precision_options()) { T::thread_default_variable_precision_options(variable_precision_options::preserve_source_precision); } ~scoped_source_precision() { T::thread_default_variable_precision_options(opts); } }; template <class T> struct scoped_source_precision<T, false> {}; template <class T, bool = boost::multiprecision::detail::is_variable_precision<T>::value> struct scoped_precision_options { unsigned saved_digits; boost::multiprecision::variable_precision_options saved_options; scoped_precision_options(unsigned digits) : saved_digits(T::thread_default_precision()), saved_options(T::thread_default_variable_precision_options()) { T::thread_default_precision(digits); } scoped_precision_options(unsigned digits, variable_precision_options opts) : saved_digits(T::thread_default_precision()), saved_options(T::thread_default_variable_precision_options()) { T::thread_default_precision(digits); T::thread_default_variable_precision_options(opts); } template <class U> scoped_precision_options(const U& u) : saved_digits(T::thread_default_precision()), saved_options(T::thread_default_variable_precision_options()) { T::thread_default_precision(u.precision()); T::thread_default_variable_precision_options(U::thread_default_variable_precision_options()); } ~scoped_precision_options() { T::thread_default_variable_precision_options(saved_options); T::thread_default_precision(saved_digits); } }; template <class T> struct scoped_precision_options<T, false> { scoped_precision_options(unsigned) {} scoped_precision_options(unsigned, variable_precision_options) {} template <class U> scoped_precision_options(const U&) {} ~scoped_precision_options() {} }; } } } // namespace boost::multiprecision::detail #endif // BOOST_MP_DETAIL_PRECISION_HPP