precision.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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_DETAIL_PRECISION_HPP
  6. #define BOOST_MP_DETAIL_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. #include <boost/multiprecision/detail/assert.hpp>
  11. namespace boost { namespace multiprecision { namespace detail {
  12. template <class B, boost::multiprecision::expression_template_option ET>
  13. inline constexpr unsigned current_precision_of_last_chance_imp(const boost::multiprecision::number<B, ET>&, const std::integral_constant<int, 0>&)
  14. {
  15. return std::numeric_limits<boost::multiprecision::number<B, ET> >::digits10;
  16. }
  17. template <class B, boost::multiprecision::expression_template_option ET>
  18. 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>&)
  19. {
  20. //
  21. // We have an arbitrary precision integer, take it's "precision" as the
  22. // location of the most-significant-bit less the location of the
  23. // least-significant-bit, ie the number of bits required to represent the
  24. // the value assuming we will have an exponent to shift things by:
  25. //
  26. return static_cast<unsigned>(val.is_zero() ? 1 : 1 + digits2_2_10(msb(abs(val)) - lsb(abs(val)) + 1));
  27. }
  28. template <class B, boost::multiprecision::expression_template_option ET>
  29. 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>&)
  30. {
  31. //
  32. // We have an arbitrary precision rational, take it's "precision" as the
  33. // the larger of the "precision" of numerator and denominator:
  34. //
  35. 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>()));
  36. }
  37. template <class B, boost::multiprecision::expression_template_option ET>
  38. inline BOOST_MP_CXX14_CONSTEXPR unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const std::integral_constant<bool, true>&)
  39. {
  40. return n.precision();
  41. }
  42. template <class B, boost::multiprecision::expression_template_option ET>
  43. inline constexpr unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& val, const std::integral_constant<bool, false>&)
  44. {
  45. using tag = std::integral_constant<int,
  46. std::numeric_limits<boost::multiprecision::number<B, ET> >::is_specialized &&
  47. std::numeric_limits<boost::multiprecision::number<B, ET> >::is_integer &&
  48. std::numeric_limits<boost::multiprecision::number<B, ET> >::is_exact &&
  49. !std::numeric_limits<boost::multiprecision::number<B, ET> >::is_modulo
  50. ? 1
  51. : boost::multiprecision::number_category<boost::multiprecision::number<B, ET> >::value == boost::multiprecision::number_kind_rational ? 2
  52. : 0>;
  53. return current_precision_of_last_chance_imp(val, tag());
  54. }
  55. template <class R, class Terminal>
  56. inline constexpr unsigned current_precision_of_terminal(const Terminal&)
  57. {
  58. return (R::thread_default_variable_precision_options() >= variable_precision_options::preserve_all_precision)
  59. ? (std::numeric_limits<Terminal>::min_exponent ? std::numeric_limits<Terminal>::digits10 : 1 + std::numeric_limits<Terminal>::digits10) : 0;
  60. }
  61. template <class R, class Terminal>
  62. inline constexpr unsigned current_precision_of(const Terminal& r)
  63. {
  64. return current_precision_of_terminal<R>(R::canonical_value(r));
  65. }
  66. template <class R>
  67. inline constexpr unsigned current_precision_of(const float&)
  68. {
  69. using list = typename R::backend_type::float_types;
  70. using first_float = typename std::tuple_element<0, list>::type;
  71. return (R::thread_default_variable_precision_options() >= variable_precision_options::preserve_all_precision) ? std::numeric_limits<first_float>::digits10 : 0;
  72. }
  73. template <class R, class Terminal, std::size_t N>
  74. inline constexpr unsigned current_precision_of(const Terminal (&)[N])
  75. { // For string literals:
  76. return 0;
  77. }
  78. template <class R, class B, boost::multiprecision::expression_template_option ET>
  79. inline constexpr unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const std::true_type&)
  80. {
  81. return std::is_same<R, boost::multiprecision::number<B, ET> >::value
  82. || (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))
  83. || (R::thread_default_variable_precision_options() >= variable_precision_options::preserve_all_precision)
  84. ? current_precision_of_imp(n, boost::multiprecision::detail::is_variable_precision<boost::multiprecision::number<B, ET> >()) : 0;
  85. }
  86. template <class R, class B, boost::multiprecision::expression_template_option ET>
  87. inline constexpr unsigned current_precision_of_imp(const boost::multiprecision::number<B, ET>& n, const std::false_type&)
  88. {
  89. return std::is_same<R, boost::multiprecision::number<B, ET> >::value
  90. || std::is_same<typename R::value_type, boost::multiprecision::number<B, ET> >::value
  91. ? current_precision_of_imp(n, boost::multiprecision::detail::is_variable_precision<boost::multiprecision::number<B, ET> >()) : 0;
  92. }
  93. template <class R, class B, boost::multiprecision::expression_template_option ET>
  94. inline constexpr unsigned current_precision_of(const boost::multiprecision::number<B, ET>& n)
  95. {
  96. return current_precision_of_imp<R>(n, boost::multiprecision::detail::is_variable_precision<R>());
  97. }
  98. template <class R, class tag, class Arg1>
  99. inline constexpr unsigned current_precision_of(const expression<tag, Arg1, void, void, void>& expr)
  100. {
  101. return current_precision_of<R>(expr.left_ref());
  102. }
  103. template <class R, class Arg1>
  104. inline constexpr unsigned current_precision_of(const expression<terminal, Arg1, void, void, void>& expr)
  105. {
  106. return current_precision_of<R>(expr.value());
  107. }
  108. template <class R, class tag, class Arg1, class Arg2>
  109. inline constexpr unsigned current_precision_of(const expression<tag, Arg1, Arg2, void, void>& expr)
  110. {
  111. return (std::max)(current_precision_of<R>(expr.left_ref()), current_precision_of<R>(expr.right_ref()));
  112. }
  113. template <class R, class tag, class Arg1, class Arg2, class Arg3>
  114. inline constexpr unsigned current_precision_of(const expression<tag, Arg1, Arg2, Arg3, void>& expr)
  115. {
  116. 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()));
  117. }
  118. #ifdef BOOST_MSVC
  119. #pragma warning(push)
  120. #pragma warning(disable : 4130)
  121. #endif
  122. template <class R, bool = boost::multiprecision::detail::is_variable_precision<R>::value>
  123. struct scoped_default_precision
  124. {
  125. template <class T>
  126. constexpr scoped_default_precision(const T&) {}
  127. template <class T, class U>
  128. constexpr scoped_default_precision(const T&, const U&) {}
  129. template <class T, class U, class V>
  130. constexpr scoped_default_precision(const T&, const U&, const V&) {}
  131. //
  132. // This function is never called: in C++17 it won't be compiled either:
  133. //
  134. unsigned precision() const
  135. {
  136. BOOST_MP_ASSERT("This function should never be called!!" == nullptr);
  137. return 0;
  138. }
  139. };
  140. #ifdef BOOST_MSVC
  141. #pragma warning(pop)
  142. #endif
  143. template <class R>
  144. struct scoped_default_precision<R, true>
  145. {
  146. template <class T>
  147. BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a)
  148. {
  149. init(has_uniform_precision() ? R::thread_default_precision() : (std::max)(R::thread_default_precision(), current_precision_of<R>(a)));
  150. }
  151. template <class T, class U>
  152. BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b)
  153. {
  154. 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))));
  155. }
  156. template <class T, class U, class V>
  157. BOOST_MP_CXX14_CONSTEXPR scoped_default_precision(const T& a, const U& b, const V& c)
  158. {
  159. 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))));
  160. }
  161. ~scoped_default_precision()
  162. {
  163. if(m_new_prec != m_old_prec)
  164. R::thread_default_precision(m_old_prec);
  165. }
  166. BOOST_MP_CXX14_CONSTEXPR unsigned precision() const
  167. {
  168. return m_new_prec;
  169. }
  170. static constexpr bool has_uniform_precision()
  171. {
  172. return R::thread_default_variable_precision_options() <= boost::multiprecision::variable_precision_options::assume_uniform_precision;
  173. }
  174. private:
  175. BOOST_MP_CXX14_CONSTEXPR void init(unsigned p)
  176. {
  177. m_old_prec = R::thread_default_precision();
  178. if (p && (p != m_old_prec))
  179. {
  180. R::thread_default_precision(p);
  181. m_new_prec = p;
  182. }
  183. else
  184. m_new_prec = m_old_prec;
  185. }
  186. unsigned m_old_prec, m_new_prec;
  187. };
  188. template <class T>
  189. inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T*, const std::integral_constant<bool, false>&) {}
  190. template <class T>
  191. inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T* obj, const std::integral_constant<bool, true>&)
  192. {
  193. if (obj->precision() != T::thread_default_precision())
  194. {
  195. obj->precision(T::thread_default_precision());
  196. }
  197. }
  198. template <class T>
  199. inline BOOST_MP_CXX14_CONSTEXPR void maybe_promote_precision(T* obj)
  200. {
  201. maybe_promote_precision(obj, std::integral_constant<bool, boost::multiprecision::detail::is_variable_precision<T>::value>());
  202. }
  203. #ifndef BOOST_NO_CXX17_IF_CONSTEXPR
  204. #define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) \
  205. if \
  206. constexpr(boost::multiprecision::detail::is_variable_precision<T>::value)
  207. #else
  208. #define BOOST_MP_CONSTEXPR_IF_VARIABLE_PRECISION(T) if (boost::multiprecision::detail::is_variable_precision<T>::value)
  209. #endif
  210. template <class T, bool = boost::multiprecision::detail::is_variable_precision<T>::value>
  211. struct scoped_target_precision
  212. {
  213. variable_precision_options opts;
  214. scoped_target_precision() : opts(T::thread_default_variable_precision_options())
  215. {
  216. T::thread_default_variable_precision_options(variable_precision_options::preserve_target_precision);
  217. }
  218. ~scoped_target_precision()
  219. {
  220. T::thread_default_variable_precision_options(opts);
  221. }
  222. };
  223. template <class T>
  224. struct scoped_target_precision<T, false> {};
  225. template <class T, bool = boost::multiprecision::detail::is_variable_precision<T>::value>
  226. struct scoped_source_precision
  227. {
  228. variable_precision_options opts;
  229. scoped_source_precision() : opts(T::thread_default_variable_precision_options())
  230. {
  231. T::thread_default_variable_precision_options(variable_precision_options::preserve_source_precision);
  232. }
  233. ~scoped_source_precision()
  234. {
  235. T::thread_default_variable_precision_options(opts);
  236. }
  237. };
  238. template <class T>
  239. struct scoped_source_precision<T, false> {};
  240. template <class T, bool = boost::multiprecision::detail::is_variable_precision<T>::value>
  241. struct scoped_precision_options
  242. {
  243. unsigned saved_digits;
  244. boost::multiprecision::variable_precision_options saved_options;
  245. scoped_precision_options(unsigned digits)
  246. : saved_digits(T::thread_default_precision()), saved_options(T::thread_default_variable_precision_options())
  247. {
  248. T::thread_default_precision(digits);
  249. }
  250. scoped_precision_options(unsigned digits, variable_precision_options opts)
  251. : saved_digits(T::thread_default_precision()), saved_options(T::thread_default_variable_precision_options())
  252. {
  253. T::thread_default_precision(digits);
  254. T::thread_default_variable_precision_options(opts);
  255. }
  256. template <class U>
  257. scoped_precision_options(const U& u)
  258. : saved_digits(T::thread_default_precision()), saved_options(T::thread_default_variable_precision_options())
  259. {
  260. T::thread_default_precision(u.precision());
  261. T::thread_default_variable_precision_options(U::thread_default_variable_precision_options());
  262. }
  263. ~scoped_precision_options()
  264. {
  265. T::thread_default_variable_precision_options(saved_options);
  266. T::thread_default_precision(saved_digits);
  267. }
  268. };
  269. template <class T>
  270. struct scoped_precision_options<T, false>
  271. {
  272. scoped_precision_options(unsigned) {}
  273. scoped_precision_options(unsigned, variable_precision_options) {}
  274. template <class U>
  275. scoped_precision_options(const U&) {}
  276. ~scoped_precision_options() {}
  277. };
  278. }
  279. }
  280. } // namespace boost::multiprecision::detail
  281. #endif // BOOST_MP_DETAIL_PRECISION_HPP