1 /////////////////////////////////////////////////////////////////////////////// 2 // Copyright 2018 John Maddock. Distributed under the Boost 3 // Software License, Version 1.0. (See accompanying file 4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 #ifndef BOOST_MP_PRECISION_HPP 7 #define BOOST_MP_PRECISION_HPP 8 9 #include <boost/multiprecision/traits/is_variable_precision.hpp> 10 #include <boost/multiprecision/detail/number_base.hpp> 11 #include <boost/multiprecision/detail/digits.hpp> 12 13 namespace boost{ namespace multiprecision{ namespace detail{ 14 15 template <class B, boost::multiprecision::expression_template_option ET> current_precision_of_last_chance_imp(const boost::multiprecision::number<B,ET> &,const mpl::false_ &)16 inline BOOST_CONSTEXPR unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>&, const mpl::false_&) 17 { 18 return std::numeric_limits<boost::multiprecision::number<B, ET> >::digits10; 19 } 20 template <class B, boost::multiprecision::expression_template_option ET> current_precision_of_last_chance_imp(const boost::multiprecision::number<B,ET> & val,const mpl::true_ &)21 inline unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>& val, const mpl::true_&) 22 { 23 // 24 // We have an arbitrary precision integer, take it's "precision" as the 25 // location of the most-significant-bit less the location of the 26 // least-significant-bit, ie the number of bits required to represent the 27 // the value assuming we will have an exponent to shift things by: 28 // 29 return val.is_zero() ? 1 : digits2_2_10(msb(abs(val)) - lsb(abs(val)) + 1); 30 } 31 32 33 template <class B, boost::multiprecision::expression_template_option ET> current_precision_of_imp(const boost::multiprecision::number<B,ET> & n,const mpl::true_ &)34 inline unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const mpl::true_&) 35 { 36 return n.precision(); 37 } 38 template <class B, boost::multiprecision::expression_template_option ET> current_precision_of_imp(const boost::multiprecision::number<B,ET> & val,const mpl::false_ &)39 inline BOOST_CONSTEXPR unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& val, const mpl::false_&) 40 { 41 return current_precision_of_last_chance_imp(val, 42 mpl::bool_< 43 std::numeric_limits<boost::multiprecision::number<B, ET> >::is_specialized 44 && std::numeric_limits<boost::multiprecision::number<B, ET> >::is_integer 45 && std::numeric_limits<boost::multiprecision::number<B, ET> >::is_exact 46 && !std::numeric_limits<boost::multiprecision::number<B, ET> >::is_modulo>()); 47 } 48 49 template <class Terminal> current_precision_of(const Terminal &)50 inline BOOST_CONSTEXPR unsigned current_precision_of(const Terminal&) 51 { 52 return std::numeric_limits<Terminal>::digits10; 53 } 54 55 template <class Terminal, std::size_t N> current_precision_of(const Terminal (&)[N])56 inline BOOST_CONSTEXPR unsigned current_precision_of(const Terminal(&)[N]) 57 { // For string literals: 58 return 0; 59 } 60 61 template <class B, boost::multiprecision::expression_template_option ET> current_precision_of(const boost::multiprecision::number<B,ET> & n)62 inline BOOST_CONSTEXPR unsigned current_precision_of(const boost::multiprecision::number<B, ET>& n) 63 { 64 return current_precision_of_imp(n, boost::multiprecision::detail::is_variable_precision<boost::multiprecision::number<B, ET> >()); 65 } 66 67 template<class tag, class Arg1> current_precision_of(const expression<tag,Arg1,void,void,void> & expr)68 inline BOOST_CONSTEXPR unsigned current_precision_of(const expression<tag, Arg1, void, void, void>& expr) 69 { 70 return current_precision_of(expr.left_ref()); 71 } 72 73 template<class Arg1> current_precision_of(const expression<terminal,Arg1,void,void,void> & expr)74 inline BOOST_CONSTEXPR unsigned current_precision_of(const expression<terminal, Arg1, void, void, void>& expr) 75 { 76 return current_precision_of(expr.value()); 77 } 78 79 template <class tag, class Arg1, class Arg2> current_precision_of(const expression<tag,Arg1,Arg2,void,void> & expr)80 inline BOOST_CONSTEXPR unsigned current_precision_of(const expression<tag, Arg1, Arg2, void, void>& expr) 81 { 82 return (std::max)(current_precision_of(expr.left_ref()), current_precision_of(expr.right_ref())); 83 } 84 85 template <class tag, class Arg1, class Arg2, class Arg3> current_precision_of(const expression<tag,Arg1,Arg2,Arg3,void> & expr)86 inline BOOST_CONSTEXPR unsigned current_precision_of(const expression<tag, Arg1, Arg2, Arg3, void>& expr) 87 { 88 return (std::max)((std::max)(current_precision_of(expr.left_ref()), current_precision_of(expr.right_ref())), current_precision_of(expr.middle_ref())); 89 } 90 91 #ifdef BOOST_MSVC 92 #pragma warning(push) 93 #pragma warning(disable:4130) 94 #endif 95 96 template <class R, bool = boost::multiprecision::detail::is_variable_precision<R>::value> 97 struct scoped_default_precision 98 { 99 template <class T> scoped_default_precisionboost::multiprecision::detail::scoped_default_precision100 BOOST_CONSTEXPR scoped_default_precision(const T&) {} 101 template <class T, class U> scoped_default_precisionboost::multiprecision::detail::scoped_default_precision102 BOOST_CONSTEXPR scoped_default_precision(const T&, const U&) {} 103 template <class T, class U, class V> scoped_default_precisionboost::multiprecision::detail::scoped_default_precision104 BOOST_CONSTEXPR scoped_default_precision(const T&, const U&, const V&) {} 105 106 // 107 // This function is never called: in C++17 it won't be compiled either: 108 // precisionboost::multiprecision::detail::scoped_default_precision109 unsigned precision()const 110 { 111 BOOST_ASSERT("This function should never be called!!" == 0); 112 return 0; 113 } 114 }; 115 116 #ifdef BOOST_MSVC 117 #pragma warning(pop) 118 #endif 119 120 template <class R> 121 struct scoped_default_precision<R, true> 122 { 123 template <class T> scoped_default_precisionboost::multiprecision::detail::scoped_default_precision124 BOOST_CXX14_CONSTEXPR scoped_default_precision(const T& a) 125 { 126 init(current_precision_of(a)); 127 } 128 template <class T, class U> scoped_default_precisionboost::multiprecision::detail::scoped_default_precision129 BOOST_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b) 130 { 131 init((std::max)(current_precision_of(a), current_precision_of(b))); 132 } 133 template <class T, class U, class V> scoped_default_precisionboost::multiprecision::detail::scoped_default_precision134 BOOST_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b, const V& c) 135 { 136 init((std::max)((std::max)(current_precision_of(a), current_precision_of(b)), current_precision_of(c))); 137 } ~scoped_default_precisionboost::multiprecision::detail::scoped_default_precision138 ~scoped_default_precision() 139 { 140 R::default_precision(m_old_prec); 141 } precisionboost::multiprecision::detail::scoped_default_precision142 BOOST_CXX14_CONSTEXPR unsigned precision()const 143 { 144 return m_new_prec; 145 } 146 private: initboost::multiprecision::detail::scoped_default_precision147 BOOST_CXX14_CONSTEXPR void init(unsigned p) 148 { 149 m_old_prec = R::default_precision(); 150 if (p) 151 { 152 R::default_precision(p); 153 m_new_prec = p; 154 } 155 else 156 m_new_prec = m_old_prec; 157 } 158 unsigned m_old_prec, m_new_prec; 159 }; 160 161 template <class T> maybe_promote_precision(T *,const mpl::false_ &)162 inline void maybe_promote_precision(T*, const mpl::false_&){} 163 164 template <class T> maybe_promote_precision(T * obj,const mpl::true_ &)165 inline void maybe_promote_precision(T* obj, const mpl::true_&) 166 { 167 if (obj->precision() != T::default_precision()) 168 { 169 obj->precision(T::default_precision()); 170 } 171 } 172 173 template <class T> maybe_promote_precision(T * obj)174 inline void maybe_promote_precision(T* obj) 175 { 176 maybe_promote_precision(obj, boost::multiprecision::detail::is_variable_precision<T>()); 177 } 178 179 #ifndef BOOST_NO_CXX17_IF_CONSTEXPR 180 # define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) if constexpr (boost::multiprecision::detail::is_variable_precision<T>::value) 181 #else 182 # define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) if(boost::multiprecision::detail::is_variable_precision<T>::value) 183 #endif 184 185 186 } 187 } 188 } 189 190 #endif // BOOST_MP_IS_BACKEND_HPP 191