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