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