precision.hpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. #ifndef BOOST_MP_PRECISION_HPP
  6. #define BOOST_MP_PRECISION_HPP
  7. #include <boost/multiprecision/traits/is_variable_precision.hpp>
  8. #include <boost/multiprecision/detail/number_base.hpp>
  9. #include <boost/multiprecision/detail/digits.hpp>
  10. namespace boost { namespace multiprecision { namespace detail {
  11. template <class B, boost::multiprecision::expression_template_option ET>
  12. inline constexpr unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>&, const std::integral_constant<bool, false>&)
  13. {
  14. return std::numeric_limits<boost::multiprecision::number<B, ET> >::digits10;
  15. }
  16. template <class B, boost::multiprecision::expression_template_option ET>
  17. inline BOOST_MP_CXX14_CONSTEXPR unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>& val, const std::integral_constant<bool, true>&)
  18. {
  19. //
  20. // We have an arbitrary precision integer, take it's "precision" as the
  21. // location of the most-significant-bit less the location of the
  22. // least-significant-bit, ie the number of bits required to represent the
  23. // the value assuming we will have an exponent to shift things by:
  24. //
  25. return val.is_zero() ? 1 : digits2_2_10(msb(abs(val)) - lsb(abs(val)) + 1);
  26. }
  27. template <class B, boost::multiprecision::expression_template_option ET>
  28. inline BOOST_MP_CXX14_CONSTEXPR unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const std::integral_constant<bool, true>&)
  29. {
  30. return n.precision();
  31. }
  32. template <class B, boost::multiprecision::expression_template_option ET>
  33. inline constexpr unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& val, const std::integral_constant<bool, false>&)
  34. {
  35. return current_precision_of_last_chance_imp(val,
  36. std::integral_constant<bool,
  37. std::numeric_limits<boost::multiprecision::number<B, ET> >::is_specialized &&
  38. 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 > ());
  39. }
  40. template <class Terminal>
  41. inline constexpr unsigned current_precision_of(const Terminal&)
  42. {
  43. return std::numeric_limits<Terminal>::digits10;
  44. }
  45. template <class Terminal, std::size_t N>
  46. inline constexpr unsigned current_precision_of(const Terminal (&)[N])
  47. { // For string literals:
  48. return 0;
  49. }
  50. template <class B, boost::multiprecision::expression_template_option ET>
  51. inline constexpr unsigned current_precision_of(const boost::multiprecision::number<B, ET>& n)
  52. {
  53. return current_precision_of_imp(n, boost::multiprecision::detail::is_variable_precision<boost::multiprecision::number<B, ET> >());
  54. }
  55. template <class tag, class Arg1>
  56. inline constexpr unsigned current_precision_of(const expression<tag, Arg1, void, void, void>& expr)
  57. {
  58. return current_precision_of(expr.left_ref());
  59. }
  60. template <class Arg1>
  61. inline constexpr unsigned current_precision_of(const expression<terminal, Arg1, void, void, void>& expr)
  62. {
  63. return current_precision_of(expr.value());
  64. }
  65. template <class tag, class Arg1, class Arg2>
  66. inline constexpr unsigned current_precision_of(const expression<tag, Arg1, Arg2, void, void>& expr)
  67. {
  68. return (std::max)(current_precision_of(expr.left_ref()), current_precision_of(expr.right_ref()));
  69. }
  70. template <class tag, class Arg1, class Arg2, class Arg3>
  71. inline constexpr unsigned current_precision_of(const expression<tag, Arg1, Arg2, Arg3, void>& expr)
  72. {
  73. return (std::max)((std::max)(current_precision_of(expr.left_ref()), current_precision_of(expr.right_ref())), current_precision_of(expr.middle_ref()));
  74. }
  75. #ifdef BOOST_MSVC
  76. #pragma warning(push)
  77. #pragma warning(disable : 4130)
  78. #endif
  79. template <class R, bool = boost::multiprecision::detail::is_variable_precision<R>::value>
  80. struct scoped_default_precision
  81. {
  82. template <class T>
  83. constexpr scoped_default_precision(const T&) {}
  84. template <class T, class U>
  85. constexpr scoped_default_precision(const T&, const U&) {}
  86. template <class T, class U, class V>
  87. constexpr scoped_default_precision(const T&, const U&, const V&) {}
  88. //
  89. // This function is never called: in C++17 it won't be compiled either:
  90. //
  91. unsigned precision() const
  92. {
  93. BOOST_ASSERT("This function should never be called!!" == 0);
  94. return 0;
  95. }
  96. };
  97. #ifdef BOOST_MSVC
  98. #pragma warning(pop)
  99. #endif
  100. template <class R>
  101. struct scoped_default_precision<R, true>
  102. {
  103. template <class T>
  104. BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a)
  105. {
  106. init(current_precision_of(a));
  107. }
  108. template <class T, class U>
  109. BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b)
  110. {
  111. init((std::max)(current_precision_of(a), current_precision_of(b)));
  112. }
  113. template <class T, class U, class V>
  114. BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b, const V& c)
  115. {
  116. init((std::max)((std::max)(current_precision_of(a), current_precision_of(b)), current_precision_of(c)));
  117. }
  118. ~scoped_default_precision()
  119. {
  120. if(m_new_prec != m_old_prec)
  121. R::default_precision(m_old_prec);
  122. }
  123. BOOST_MP_CXX14_CONSTEXPR unsigned precision() const
  124. {
  125. return m_new_prec;
  126. }
  127. private:
  128. BOOST_MP_CXX14_CONSTEXPR void init(unsigned p)
  129. {
  130. m_old_prec = R::default_precision();
  131. if (p && (p != m_old_prec))
  132. {
  133. R::default_precision(p);
  134. m_new_prec = p;
  135. }
  136. else
  137. m_new_prec = m_old_prec;
  138. }
  139. unsigned m_old_prec, m_new_prec;
  140. };
  141. template <class T>
  142. inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T*, const std::integral_constant<bool, false>&) {}
  143. template <class T>
  144. inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T* obj, const std::integral_constant<bool, true>&)
  145. {
  146. if (obj->precision() != T::default_precision())
  147. {
  148. obj->precision(T::default_precision());
  149. }
  150. }
  151. template <class T>
  152. inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T* obj)
  153. {
  154. maybe_promote_precision(obj, std::integral_constant<bool, boost::multiprecision::detail::is_variable_precision<T>::value>());
  155. }
  156. #ifndef BOOST_NO_CXX17_IF_CONSTEXPR
  157. #define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) \
  158. if \
  159. constexpr(boost::multiprecision::detail::is_variable_precision<T>::value)
  160. #else
  161. #define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) if (boost::multiprecision::detail::is_variable_precision<T>::value)
  162. #endif
  163. }
  164. }
  165. } // namespace boost::multiprecision::detail
  166. #endif // BOOST_MP_IS_BACKEND_HPP