1 // Copyright 2016-2021 Francesco Biscani (bluescarni@gmail.com)
2 //
3 // This file is part of the mp++ library.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla
6 // Public License v. 2.0. If a copy of the MPL was not distributed
7 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9 #ifndef MPPP_DETAIL_MPFR_HPP
10 #define MPPP_DETAIL_MPFR_HPP
11
12 #include <limits>
13 #include <type_traits>
14
15 #include <mpfr.h>
16
17 #include <mp++/detail/gmp.hpp>
18 #include <mp++/detail/type_traits.hpp>
19
20 #if MPFR_VERSION_MAJOR < 3
21
22 #error Minimum supported MPFR version is 3.
23
24 #endif
25
26 #if GMP_NAIL_BITS
27
28 // See:
29 // https://gmplib.org/list-archives/gmp-discuss/2012-November/005189.html
30
31 #error MPFR is incompatible with GMP if nails are enabled.
32
33 #endif
34
35 namespace mppp
36 {
37
38 // Minimum precision for a real.
real_prec_min()39 constexpr ::mpfr_prec_t real_prec_min()
40 {
41 // NOTE: Arb wants at least 2 bits, so let's
42 // make sure we don't allow 1-bit numbers.
43 return MPFR_PREC_MIN > 2 ? MPFR_PREC_MIN : 2;
44 }
45
46 // Maximum precision for a real.
real_prec_max()47 constexpr ::mpfr_prec_t real_prec_max()
48 {
49 // For the max precision, we remove 7 bits from the MPFR_PREC_MAX value (as the MPFR docs warn
50 // to never set the precision "close" to the max value).
51 return MPFR_PREC_MAX >> 7;
52 }
53
54 // The MPFR structure underlying mpfr_t.
55 using mpfr_struct_t = std::remove_extent<::mpfr_t>::type;
56
57 namespace detail
58 {
59
60 // Paranoia checks.
61 static_assert(real_prec_min() <= real_prec_max(), "The minimum real precision is larger than the maximum precision.");
62
63 // Zero precision has a special meaning, depending on the context. Thus, the minimum precision must be nonzero.
64 static_assert(real_prec_min() > 0, "The minimum real precision must be positive.");
65
66 // Check if a precision value is in the allowed range.
real_prec_check(::mpfr_prec_t p)67 constexpr bool real_prec_check(::mpfr_prec_t p)
68 {
69 return p >= real_prec_min() && p <= real_prec_max();
70 }
71
72 // Simple RAII holder for MPFR floats.
73 struct mpfr_raii {
74 // A constructor from a precision value, will set the value to NaN.
75 // No check is performed on the precision.
76 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
mpfr_raiimppp::detail::mpfr_raii77 explicit mpfr_raii(::mpfr_prec_t prec)
78 {
79 ::mpfr_init2(&m_mpfr, prec);
80 }
81 // Disable all the other ctors/assignment ops.
82 mpfr_raii(const mpfr_raii &) = delete;
83 mpfr_raii(mpfr_raii &&) = delete;
84 mpfr_raii &operator=(const mpfr_raii &) = delete;
85 mpfr_raii &operator=(mpfr_raii &&) = delete;
~mpfr_raiimppp::detail::mpfr_raii86 ~mpfr_raii()
87 {
88 ::mpfr_clear(&m_mpfr);
89 }
90 mpfr_struct_t m_mpfr;
91 };
92
93 // A couple of sanity checks when constructing temporary mpfrs/mpfs from long double.
94 static_assert(std::numeric_limits<long double>::max_digits10 < nl_max<int>() / 4, "Overflow error.");
95 static_assert(std::numeric_limits<long double>::max_digits10 * 4 < nl_max<::mpfr_prec_t>(), "Overflow error.");
96 static_assert(std::numeric_limits<long double>::max_digits10 * 4 < nl_max<::mp_bitcnt_t>(), "Overflow error.");
97 // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)
98 static_assert(real_prec_check(static_cast<::mpfr_prec_t>(std::numeric_limits<long double>::max_digits10 * 4)),
99 "The precision required to represent long double is outside the MPFR min/max precision bounds.");
100
101 } // namespace detail
102 } // namespace mppp
103
104 #endif
105