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 #if defined(_MSC_VER)
10
11 #define _SCL_SECURE_NO_WARNINGS
12
13 #endif
14
15 // NOTE: in order to use the MPFR print functions,
16 // we must make sure that stdio.h is included before mpfr.h:
17 // https://www.mpfr.org/mpfr-current/mpfr.html#Formatted-Output-Functions
18 #include <cstdio>
19
20 #include <mp++/config.hpp>
21
22 #include <algorithm>
23 #include <array>
24 #include <cassert>
25 #include <cerrno>
26 #include <cmath>
27 #include <cstddef>
28 #include <ios>
29 #include <iostream>
30 #include <limits>
31 #include <locale>
32 #include <memory>
33 #include <sstream>
34 #include <stdexcept>
35 #include <string>
36 #include <tuple>
37 #include <type_traits>
38 #include <utility>
39 #include <vector>
40
41 #if defined(MPPP_HAVE_STRING_VIEW)
42 #include <string_view>
43 #endif
44
45 #if defined(MPPP_WITH_BOOST_S11N)
46
47 #include <boost/archive/binary_iarchive.hpp>
48 #include <boost/archive/binary_oarchive.hpp>
49 #include <boost/serialization/binary_object.hpp>
50
51 #endif
52
53 #include <mp++/detail/gmp.hpp>
54 #include <mp++/detail/mpfr.hpp>
55 #include <mp++/detail/utils.hpp>
56 #include <mp++/integer.hpp>
57 #include <mp++/real.hpp>
58
59 #if defined(MPPP_WITH_QUADMATH)
60 #include <mp++/real128.hpp>
61 #endif
62
63 namespace mppp
64 {
65
66 namespace detail
67 {
68
69 namespace
70 {
71
72 // Some misc tests to check that the mpfr struct conforms to our expectations.
73 struct expected_mpfr_struct_t {
74 ::mpfr_prec_t _mpfr_prec;
75 ::mpfr_sign_t _mpfr_sign;
76 ::mpfr_exp_t _mpfr_exp;
77 ::mp_limb_t *_mpfr_d;
78 };
79
80 static_assert(sizeof(expected_mpfr_struct_t) == sizeof(mpfr_struct_t) && offsetof(mpfr_struct_t, _mpfr_prec) == 0u
81 && offsetof(mpfr_struct_t, _mpfr_sign) == offsetof(expected_mpfr_struct_t, _mpfr_sign)
82 && offsetof(mpfr_struct_t, _mpfr_exp) == offsetof(expected_mpfr_struct_t, _mpfr_exp)
83 && offsetof(mpfr_struct_t, _mpfr_d) == offsetof(expected_mpfr_struct_t, _mpfr_d)
84 && std::is_same<::mp_limb_t *, decltype(std::declval<mpfr_struct_t>()._mpfr_d)>::value,
85 "Invalid mpfr_t struct layout and/or MPFR types.");
86
87 // Double check that real is a standard layout class.
88 static_assert(std::is_standard_layout<real>::value, "real is not a standard layout class.");
89
90 #if MPPP_CPLUSPLUS >= 201703L
91
92 // If we have C++17, we can use structured bindings to test the layout of mpfr_struct_t
93 // and its members' types.
test_mpfr_struct_t()94 constexpr bool test_mpfr_struct_t()
95 {
96 auto [prec, sign, exp, ptr] = mpfr_struct_t{};
97 static_assert(std::is_same<decltype(ptr), ::mp_limb_t *>::value);
98 ignore(prec, sign, exp, ptr);
99
100 return true;
101 }
102
103 static_assert(test_mpfr_struct_t());
104
105 #endif
106
107 } // namespace
108
109 // Wrapper for calling mpfr_lgamma().
real_lgamma_wrapper(::mpfr_t rop,const::mpfr_t op,::mpfr_rnd_t)110 void real_lgamma_wrapper(::mpfr_t rop, const ::mpfr_t op, ::mpfr_rnd_t)
111 {
112 // NOTE: we ignore the sign for consistency with lgamma.
113 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
114 int signp;
115 ::mpfr_lgamma(rop, &signp, op, MPFR_RNDN);
116 }
117
118 // Wrapper for calling mpfr_li2().
real_li2_wrapper(::mpfr_t rop,const::mpfr_t op,::mpfr_rnd_t rnd)119 void real_li2_wrapper(::mpfr_t rop, const ::mpfr_t op, ::mpfr_rnd_t rnd)
120 {
121 // NOTE: mpfr_li2() returns the *real* part of the result,
122 // which, for op >= 1, is a complex number. For consistency
123 // with other functions, if the result is complex just set
124 // rop to nan.
125 // NOTE: check for nan before checking for >= 1, otherwise
126 // the comparison function will set the erange flag.
127 if (!mpfr_nan_p(op) && mpfr_cmp_ui(op, 1u) >= 0) {
128 ::mpfr_set_nan(rop);
129 } else {
130 ::mpfr_li2(rop, op, rnd);
131 }
132 }
133
134 // Wrappers for calling integer and remainder-related functions
135 // with NaN checking.
real_ceil_wrapper(::mpfr_t rop,const::mpfr_t op)136 void real_ceil_wrapper(::mpfr_t rop, const ::mpfr_t op)
137 {
138 if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
139 throw std::domain_error("Cannot compute the ceiling of a NaN value");
140 }
141
142 mpfr_ceil(rop, op);
143 }
144
real_floor_wrapper(::mpfr_t rop,const::mpfr_t op)145 void real_floor_wrapper(::mpfr_t rop, const ::mpfr_t op)
146 {
147 if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
148 throw std::domain_error("Cannot compute the floor of a NaN value");
149 }
150
151 mpfr_floor(rop, op);
152 }
153
real_round_wrapper(::mpfr_t rop,const::mpfr_t op)154 void real_round_wrapper(::mpfr_t rop, const ::mpfr_t op)
155 {
156 if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
157 throw std::domain_error("Cannot round a NaN value");
158 }
159
160 mpfr_round(rop, op);
161 }
162
163 #if defined(MPPP_MPFR_HAVE_MPFR_ROUNDEVEN)
164
real_roundeven_wrapper(::mpfr_t rop,const::mpfr_t op)165 void real_roundeven_wrapper(::mpfr_t rop, const ::mpfr_t op)
166 {
167 if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
168 throw std::domain_error("Cannot round a NaN value");
169 }
170
171 ::mpfr_roundeven(rop, op);
172 }
173
174 #endif
175
real_trunc_wrapper(::mpfr_t rop,const::mpfr_t op)176 void real_trunc_wrapper(::mpfr_t rop, const ::mpfr_t op)
177 {
178 if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
179 throw std::domain_error("Cannot truncate a NaN value");
180 }
181
182 mpfr_trunc(rop, op);
183 }
184
real_frac_wrapper(::mpfr_t rop,const::mpfr_t op)185 void real_frac_wrapper(::mpfr_t rop, const ::mpfr_t op)
186 {
187 if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
188 throw std::domain_error("Cannot compute the fractional part of a NaN value");
189 }
190
191 ::mpfr_frac(rop, op, MPFR_RNDN);
192 }
193
mpfr_to_stream(const::mpfr_t r,std::ostream & os,int base)194 void mpfr_to_stream(const ::mpfr_t r, std::ostream &os, int base)
195 {
196 // All chars potentially used by MPFR for representing the digits up to base 62, sorted.
197 constexpr char all_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
198 // Check the base.
199 if (mppp_unlikely(base < 2 || base > 62)) {
200 throw std::invalid_argument("Cannot convert a real to a string in base " + to_string(base)
201 + ": the base must be in the [2,62] range");
202 }
203 // Special values first.
204 if (mpfr_nan_p(r)) {
205 // NOTE: up to base 16 we can use nan, inf, etc., but with larger
206 // bases we have to use the syntax with @.
207 os << (base <= 16 ? "nan" : "@nan@");
208 return;
209 }
210 if (mpfr_inf_p(r)) {
211 if (mpfr_sgn(r) < 0) {
212 os << '-';
213 }
214 os << (base <= 16 ? "inf" : "@inf@");
215 return;
216 }
217
218 // Get the string fractional representation via the MPFR function,
219 // and wrap it into a smart pointer.
220 ::mpfr_exp_t exp(0);
221 std::unique_ptr<char, void (*)(char *)> str(::mpfr_get_str(nullptr, &exp, base, 0, r, MPFR_RNDN), ::mpfr_free_str);
222 // LCOV_EXCL_START
223 if (mppp_unlikely(!str)) {
224 throw std::runtime_error("Error in the conversion of a real to string: the call to mpfr_get_str() failed");
225 }
226 // LCOV_EXCL_STOP
227
228 // Print the string, inserting a decimal point after the first digit.
229 bool dot_added = false;
230 // NOLINTNEXTLINE(llvm-qualified-auto, readability-qualified-auto)
231 for (auto cptr = str.get(); *cptr != '\0'; ++cptr) {
232 os << (*cptr);
233 if (!dot_added) {
234 if (base <= 10) {
235 // For bases up to 10, we can use the followig guarantee
236 // from the standard:
237 // http://stackoverflow.com/questions/13827180/char-ascii-relation
238 // """
239 // The mapping of integer values for characters does have one guarantee given
240 // by the Standard: the values of the decimal digits are contiguous.
241 // (i.e., '1' - '0' == 1, ... '9' - '0' == 9)
242 // """
243 // http://eel.is/c++draft/lex.charset#3
244 if (*cptr >= '0' && *cptr <= '9') {
245 os << '.';
246 dot_added = true;
247 }
248 } else {
249 // For bases larger than 10, we do a binary search among all the allowed characters.
250 // NOTE: we need to search into the whole all_chars array (instead of just up to all_chars
251 // + base) because apparently mpfr_get_str() seems to ignore lower/upper case when the base
252 // is small enough (e.g., it uses 'a' instead of 'A' when printing in base 11).
253 // NOTE: the range needs to be sizeof() - 1 because sizeof() also includes the terminator.
254 if (std::binary_search(all_chars, all_chars + (sizeof(all_chars) - 1u), *cptr)) {
255 os << '.';
256 dot_added = true;
257 }
258 }
259 }
260 }
261 assert(dot_added);
262
263 // Adjust the exponent. Do it in multiprec in order to avoid potential overflow.
264 integer<1> z_exp{exp};
265 --z_exp;
266 const auto exp_sgn = z_exp.sgn();
267 if (exp_sgn != 0 && !mpfr_zero_p(r)) {
268 // Add the exponent at the end of the string, if both the value and the exponent
269 // are nonzero.
270 // NOTE: for bases greater than 10 we need '@' for the exponent, rather than 'e' or 'E'.
271 // https://www.mpfr.org/mpfr-current/mpfr.html#Assignment-Functions
272 os << (base <= 10 ? 'e' : '@');
273 if (exp_sgn == 1) {
274 // Add extra '+' if the exponent is positive, for consistency with
275 // real128's string format (and possibly other formats too?).
276 os << '+';
277 }
278 os << z_exp;
279 }
280 }
281
282 } // namespace detail
283
284 // Default constructor.
285 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real()286 real::real()
287 {
288 ::mpfr_init2(&m_mpfr, real_prec_min());
289 ::mpfr_set_zero(&m_mpfr, 1);
290 }
291
292 // Init a real with precision p, setting its value to nan. No precision
293 // checking is performed.
294 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const ptag &,::mpfr_prec_t p,bool ignore_prec)295 real::real(const ptag &, ::mpfr_prec_t p, bool ignore_prec)
296 {
297 assert(ignore_prec);
298 assert(detail::real_prec_check(p));
299 detail::ignore(ignore_prec);
300 ::mpfr_init2(&m_mpfr, p);
301 }
302
303 // Copy constructor.
real(const real & other)304 real::real(const real &other) : real(&other.m_mpfr) {}
305
306 // Copy constructor with custom precision.
307 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const real & other,::mpfr_prec_t p)308 real::real(const real &other, ::mpfr_prec_t p)
309 {
310 // Init with custom precision, and then set.
311 ::mpfr_init2(&m_mpfr, check_init_prec(p));
312 mpfr_set(&m_mpfr, &other.m_mpfr, MPFR_RNDN);
313 }
314
315 // Move constructor with custom precision.
316 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(real && other,::mpfr_prec_t p)317 real::real(real &&other, ::mpfr_prec_t p)
318 {
319 // Check the precision first of all.
320 check_init_prec(p);
321
322 // Shallow copy other.
323 m_mpfr = other.m_mpfr;
324 // Mark the other as moved-from.
325 other.m_mpfr._mpfr_d = nullptr;
326
327 // Apply the precision.
328 prec_round_impl<false>(p);
329 }
330
331 // Construction from FPs.
332 template <typename Func, typename T>
dispatch_fp_construction(const Func & func,const T & x)333 void real::dispatch_fp_construction(const Func &func, const T &x)
334 {
335 func(&m_mpfr, x, MPFR_RNDN);
336 }
337
dispatch_construction(const float & x)338 void real::dispatch_construction(const float &x)
339 {
340 dispatch_fp_construction(::mpfr_set_flt, x);
341 }
342
dispatch_construction(const double & x)343 void real::dispatch_construction(const double &x)
344 {
345 dispatch_fp_construction(::mpfr_set_d, x);
346 }
347
dispatch_construction(const long double & x)348 void real::dispatch_construction(const long double &x)
349 {
350 dispatch_fp_construction(::mpfr_set_ld, x);
351 }
352
353 // Special casing for bool, otherwise MSVC warns if we fold this into the
354 // constructor from unsigned.
dispatch_construction(const bool & b)355 void real::dispatch_construction(const bool &b)
356 {
357 mpfr_set_ui(&m_mpfr, static_cast<unsigned long>(b), MPFR_RNDN);
358 }
359
dispatch_mpz_construction(const::mpz_t n)360 void real::dispatch_mpz_construction(const ::mpz_t n)
361 {
362 ::mpfr_set_z(&m_mpfr, n, MPFR_RNDN);
363 }
364
dispatch_mpq_construction(const::mpq_t q)365 void real::dispatch_mpq_construction(const ::mpq_t q)
366 {
367 ::mpfr_set_q(&m_mpfr, q, MPFR_RNDN);
368 }
369
370 #if defined(MPPP_WITH_QUADMATH)
371
dispatch_construction(const real128 & x)372 void real::dispatch_construction(const real128 &x)
373 {
374 assign_real128(x);
375 }
376
377 #endif
378
379 // Various helpers and constructors from string-like entities.
construct_from_c_string(const char * s,int base,::mpfr_prec_t p)380 void real::construct_from_c_string(const char *s, int base, ::mpfr_prec_t p)
381 {
382 if (mppp_unlikely(base && (base < 2 || base > 62))) {
383 throw std::invalid_argument("Cannot construct a real from a string in base " + detail::to_string(base)
384 + ": the base must either be zero or in the [2,62] range");
385 }
386 ::mpfr_init2(&m_mpfr, check_init_prec(p));
387 const auto ret = ::mpfr_set_str(&m_mpfr, s, base, MPFR_RNDN);
388 if (mppp_unlikely(ret == -1)) {
389 ::mpfr_clear(&m_mpfr);
390 throw std::invalid_argument(std::string{"The string '"} + s + "' does not represent a valid real in base "
391 + detail::to_string(base));
392 }
393 }
394
395 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const ptag &,const char * s,int base,::mpfr_prec_t p)396 real::real(const ptag &, const char *s, int base, ::mpfr_prec_t p)
397 {
398 construct_from_c_string(s, base, p);
399 }
400
real(const ptag &,const std::string & s,int base,::mpfr_prec_t p)401 real::real(const ptag &, const std::string &s, int base, ::mpfr_prec_t p) : real(s.c_str(), base, p) {}
402
403 #if defined(MPPP_HAVE_STRING_VIEW)
real(const ptag &,const std::string_view & s,int base,::mpfr_prec_t p)404 real::real(const ptag &, const std::string_view &s, int base, ::mpfr_prec_t p)
405 : real(s.data(), s.data() + s.size(), base, p)
406 {
407 }
408 #endif
409
410 // Constructor from range of characters, base and precision.
411 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const char * begin,const char * end,int base,::mpfr_prec_t p)412 real::real(const char *begin, const char *end, int base, ::mpfr_prec_t p)
413 {
414 MPPP_MAYBE_TLS std::vector<char> buffer;
415 buffer.assign(begin, end);
416 buffer.emplace_back('\0');
417 construct_from_c_string(buffer.data(), base, p);
418 }
419
420 // Constructor from range of characters and precision.
real(const char * begin,const char * end,::mpfr_prec_t p)421 real::real(const char *begin, const char *end, ::mpfr_prec_t p) : real(begin, end, 10, p) {}
422
423 // Constructor from a special value, sign and precision.
424 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(real_kind k,int sign,::mpfr_prec_t p)425 real::real(real_kind k, int sign, ::mpfr_prec_t p)
426 {
427 ::mpfr_init2(&m_mpfr, check_init_prec(p));
428 // NOTE: handle all cases explicitly, in order to avoid
429 // compiler warnings.
430 switch (k) {
431 case real_kind::nan:
432 break;
433 case real_kind::inf:
434 set_inf(sign);
435 break;
436 case real_kind::zero:
437 set_zero(sign);
438 break;
439 default:
440 // Clean up before throwing.
441 ::mpfr_clear(&m_mpfr);
442 using kind_cast_t = std::underlying_type<::mpfr_kind_t>::type;
443 throw std::invalid_argument(
444 "The 'real_kind' value passed to the constructor of a real ("
445 + std::to_string(static_cast<kind_cast_t>(k)) + ") is not one of the three allowed values ('nan'="
446 + std::to_string(static_cast<kind_cast_t>(real_kind::nan))
447 + ", 'inf'=" + std::to_string(static_cast<kind_cast_t>(real_kind::inf))
448 + " and 'zero'=" + std::to_string(static_cast<kind_cast_t>(real_kind::zero)) + ")");
449 }
450 }
451
452 // Constructor from a special value and precision.
real(real_kind k,::mpfr_prec_t p)453 real::real(real_kind k, ::mpfr_prec_t p) : real(k, 0, p) {}
454
455 // Constructors from n*2**e.
456 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(unsigned long n,::mpfr_exp_t e,::mpfr_prec_t p)457 real::real(unsigned long n, ::mpfr_exp_t e, ::mpfr_prec_t p)
458 {
459 ::mpfr_init2(&m_mpfr, check_init_prec(p));
460 set_ui_2exp(*this, n, e);
461 }
462
463 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(long n,::mpfr_exp_t e,::mpfr_prec_t p)464 real::real(long n, ::mpfr_exp_t e, ::mpfr_prec_t p)
465 {
466 ::mpfr_init2(&m_mpfr, check_init_prec(p));
467 set_si_2exp(*this, n, e);
468 }
469
470 // Copy constructor from mpfr_t.
471 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const::mpfr_t x)472 real::real(const ::mpfr_t x)
473 {
474 // Init with the same precision as other, and then set.
475 ::mpfr_init2(&m_mpfr, mpfr_get_prec(x));
476 mpfr_set(&m_mpfr, x, MPFR_RNDN);
477 }
478
479 // Copy assignment operator.
operator =(const real & other)480 real &real::operator=(const real &other)
481 {
482 if (mppp_likely(this != &other)) {
483 if (is_valid()) {
484 // this has not been moved-from.
485 // Copy the precision. This will also reset the internal value.
486 // No need for prec checking as we assume other has a valid prec.
487 set_prec_impl<false>(other.get_prec());
488 } else {
489 // this has been moved-from: init before setting.
490 ::mpfr_init2(&m_mpfr, other.get_prec());
491 }
492 // Perform the actual copy from other.
493 mpfr_set(&m_mpfr, &other.m_mpfr, MPFR_RNDN);
494 }
495 return *this;
496 }
497
498 // Implementation of the assignment from string.
string_assignment_impl(const char * s,int base)499 void real::string_assignment_impl(const char *s, int base)
500 {
501 if (mppp_unlikely(base && (base < 2 || base > 62))) {
502 throw std::invalid_argument("Cannot assign a real from a string in base " + detail::to_string(base)
503 + ": the base must either be zero or in the [2,62] range");
504 }
505 const auto ret = ::mpfr_set_str(&m_mpfr, s, base, MPFR_RNDN);
506 if (mppp_unlikely(ret == -1)) {
507 ::mpfr_set_nan(&m_mpfr);
508 throw std::invalid_argument(std::string{"The string '"} + s
509 + "' cannot be interpreted as a floating-point value in base "
510 + detail::to_string(base));
511 }
512 }
513
514 #if defined(MPPP_WITH_QUADMATH)
515
516 // Implementation of real128's assignment
517 // from real.
operator =(const real & x)518 real128 &real128::operator=(const real &x)
519 {
520 // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
521 return *this = static_cast<real128>(x);
522 }
523
524 #endif
525
526 // Copy assignment from mpfr_t.
operator =(const::mpfr_t x)527 real &real::operator=(const ::mpfr_t x)
528 {
529 // Set the precision, assuming the prec of x is valid.
530 set_prec_impl<false>(mpfr_get_prec(x));
531 // Set the value.
532 mpfr_set(&m_mpfr, x, MPFR_RNDN);
533 return *this;
534 }
535
536 #if !defined(_MSC_VER) || defined(__clang__)
537
538 // Move assignment from mpfr_t.
operator =(::mpfr_t && x)539 real &real::operator=(::mpfr_t &&x)
540 {
541 // Clear this.
542 ::mpfr_clear(&m_mpfr);
543 // Shallow copy x.
544 m_mpfr = *x;
545 return *this;
546 }
547
548 #endif
549
550 // Set to another real.
set(const real & other)551 real &real::set(const real &other)
552 {
553 return set(&other.m_mpfr);
554 }
555
556 // Implementation of string setters.
set_impl(const char * s,int base)557 real &real::set_impl(const char *s, int base)
558 {
559 string_assignment_impl(s, base);
560 return *this;
561 }
562
set_impl(const std::string & s,int base)563 real &real::set_impl(const std::string &s, int base)
564 {
565 return set(s.c_str(), base);
566 }
567
568 #if defined(MPPP_HAVE_STRING_VIEW)
set_impl(const std::string_view & s,int base)569 real &real::set_impl(const std::string_view &s, int base)
570 {
571 return set(s.data(), s.data() + s.size(), base);
572 }
573 #endif
574
575 // Set to character range.
set(const char * begin,const char * end,int base)576 real &real::set(const char *begin, const char *end, int base)
577 {
578 MPPP_MAYBE_TLS std::vector<char> buffer;
579 buffer.assign(begin, end);
580 buffer.emplace_back('\0');
581 return set(buffer.data(), base);
582 }
583
584 // Set to an mpfr_t.
set(const::mpfr_t x)585 real &real::set(const ::mpfr_t x)
586 {
587 mpfr_set(&m_mpfr, x, MPFR_RNDN);
588 return *this;
589 }
590
591 // Set to NaN.
set_nan()592 real &real::set_nan()
593 {
594 ::mpfr_set_nan(&m_mpfr);
595 return *this;
596 }
597
598 // Set to infinity.
set_inf(int sign)599 real &real::set_inf(int sign)
600 {
601 ::mpfr_set_inf(&m_mpfr, sign);
602 return *this;
603 }
604
605 // Set to zero.
set_zero(int sign)606 real &real::set_zero(int sign)
607 {
608 ::mpfr_set_zero(&m_mpfr, sign);
609 return *this;
610 }
611
612 // Detect one.
is_one() const613 bool real::is_one() const
614 {
615 // NOTE: preempt calling the comparison function, if this is NaN
616 // (in such case, the range flag will be touched and we do not
617 // want to bother with that).
618 return !nan_p() && (mpfr_cmp_ui(&m_mpfr, 1u) == 0);
619 }
620
621 #if defined(MPPP_WITH_QUADMATH)
622
623 namespace detail
624 {
625
626 // Some private machinery for the interaction between real and real128.
627 namespace
628 {
629
630 // Helper to create a real with the value 2**112. This represents the "hidden bit"
631 // of the significand of a quadruple-precision FP.
real_2_112()632 real real_2_112()
633 {
634 // NOTE: we need only 1 bit of precision,
635 // but make sure that we don't choose a value
636 // too small.
637 real retval{1, clamp_mpfr_prec(1)};
638
639 // NOTE: do the computation using the MPFR API,
640 // in order to avoid mp++'s precision management.
641 ::mpfr_mul_2ui(retval._get_mpfr_t(), retval.get_mpfr_t(), 112u, MPFR_RNDN);
642
643 return retval;
644 }
645
646 } // namespace
647
648 } // namespace detail
649
assign_real128(const real128 & x)650 void real::assign_real128(const real128 &x)
651 {
652 // Get the IEEE repr. of x.
653 const auto t = x.get_ieee();
654 // A utility function to write the significand of x
655 // as a big integer inside m_mpfr.
656 auto write_significand = [this, &t]() {
657 // The 4 32-bits part of the significand, from most to least
658 // significant digits.
659 const auto p1 = std::get<2>(t) >> 32;
660 const auto p2 = std::get<2>(t) % (1ull << 32);
661 const auto p3 = std::get<3>(t) >> 32;
662 const auto p4 = std::get<3>(t) % (1ull << 32);
663 // Build the significand, from most to least significant.
664 // NOTE: unsigned long is guaranteed to be at least 32 bit.
665 mpfr_set_ui(&this->m_mpfr, static_cast<unsigned long>(p1), MPFR_RNDN);
666 ::mpfr_mul_2ui(&this->m_mpfr, &this->m_mpfr, 32ul, MPFR_RNDN);
667 ::mpfr_add_ui(&this->m_mpfr, &this->m_mpfr, static_cast<unsigned long>(p2), MPFR_RNDN);
668 ::mpfr_mul_2ui(&this->m_mpfr, &this->m_mpfr, 32ul, MPFR_RNDN);
669 ::mpfr_add_ui(&this->m_mpfr, &this->m_mpfr, static_cast<unsigned long>(p3), MPFR_RNDN);
670 ::mpfr_mul_2ui(&this->m_mpfr, &this->m_mpfr, 32ul, MPFR_RNDN);
671 ::mpfr_add_ui(&this->m_mpfr, &this->m_mpfr, static_cast<unsigned long>(p4), MPFR_RNDN);
672 };
673 // Check if the significand is zero.
674 const bool sig_zero = std::get<2>(t) == 0u && std::get<3>(t) == 0u;
675 if (std::get<1>(t) == 0u) {
676 // Zero or subnormal numbers.
677 if (sig_zero) {
678 // Zero.
679 ::mpfr_set_zero(&m_mpfr, 1);
680 } else {
681 // Subnormal.
682 write_significand();
683 ::mpfr_div_2ui(&m_mpfr, &m_mpfr, 16382ul + 112ul, MPFR_RNDN);
684 }
685 } else if (std::get<1>(t) == 32767u) {
686 // NaN or inf.
687 if (sig_zero) {
688 // inf.
689 ::mpfr_set_inf(&m_mpfr, 1);
690 } else {
691 // NaN.
692 ::mpfr_set_nan(&m_mpfr);
693 }
694 } else {
695 // Write the significand into this.
696 write_significand();
697 // Add the hidden bit on top.
698 const MPPP_MAYBE_TLS real r_2_112 = detail::real_2_112();
699 ::mpfr_add(&m_mpfr, &m_mpfr, r_2_112.get_mpfr_t(), MPFR_RNDN);
700 // Multiply by 2 raised to the adjusted exponent.
701 ::mpfr_mul_2si(&m_mpfr, &m_mpfr, static_cast<long>(std::get<1>(t)) - (16383l + 112), MPFR_RNDN);
702 }
703 if (std::get<0>(t) != 0u) {
704 // Negate if the sign bit is set.
705 ::mpfr_neg(&m_mpfr, &m_mpfr, MPFR_RNDN);
706 }
707 }
708
convert_to_real128() const709 real128 real::convert_to_real128() const
710 {
711 // Handle the special cases first.
712 if (nan_p()) {
713 return real128_nan();
714 }
715 // NOTE: the number 2**18 = 262144 is chosen so that it's amply outside the exponent
716 // range of real128 (a 15 bit value with some offset) but well within the
717 // range of long (around +-2**31 guaranteed by the standard).
718 //
719 // NOTE: the reason why we do these checks with large positive and negative exponents
720 // is that they ensure we can safely convert _mpfr_exp to long later.
721 if (inf_p() || m_mpfr._mpfr_exp > (1l << 18)) {
722 return sgn() > 0 ? real128_inf() : -real128_inf();
723 }
724 if (zero_p() || m_mpfr._mpfr_exp < -(1l << 18)) {
725 // Preserve the signedness of zero.
726 return signbit() ? -real128{} : real128{};
727 }
728 // NOTE: this is similar to the code in real128.hpp for the constructor from integer,
729 // with some modification due to the different padding in MPFR vs GMP (see below).
730 const auto prec = get_prec();
731 // Number of limbs in this.
732 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
733 auto nlimbs = prec / ::mp_bits_per_limb + static_cast<bool>(prec % ::mp_bits_per_limb);
734 assert(nlimbs != 0);
735 // NOTE: in MPFR the most significant (nonzero) bit of the significand
736 // is always at the high end of the most significand limb. In other words,
737 // MPFR pads the multiprecision significand on the right, the opposite
738 // of GMP integers (which have padding on the left, i.e., in the top limb).
739 //
740 // NOTE: contrary to real128, the MPFR format does not have a hidden bit on top.
741 //
742 // Init retval with the highest limb.
743 //
744 // NOTE: MPFR does not support nail builds in GMP, so we don't have to worry about that.
745 real128 retval{m_mpfr._mpfr_d[--nlimbs]};
746 // Init the number of read bits.
747 // NOTE: we have read a full limb in the line above, so mp_bits_per_limb bits. If mp_bits_per_limb > 113,
748 // then the constructor of real128 truncated the input limb value to 113 bits of precision, so effectively
749 // we have read 113 bits only in such a case.
750 unsigned read_bits = detail::c_min(static_cast<unsigned>(::mp_bits_per_limb), real128_sig_digits());
751 while (nlimbs != 0 && read_bits < real128_sig_digits()) {
752 // Number of bits to be read from the current limb. Either mp_bits_per_limb or less.
753 const unsigned rbits
754 = detail::c_min(static_cast<unsigned>(::mp_bits_per_limb), real128_sig_digits() - read_bits);
755 // Shift up by rbits.
756 // NOTE: cast to int is safe, as rbits is no larger than mp_bits_per_limb which is
757 // representable by int.
758 retval = scalbn(retval, static_cast<int>(rbits));
759 // Add the next limb, removing lower bits if they are not to be read.
760 retval += m_mpfr._mpfr_d[--nlimbs] >> (static_cast<unsigned>(::mp_bits_per_limb) - rbits);
761 // Update the number of read bits.
762 // NOTE: due to the definition of rbits, read_bits can never reach past real128_sig_digits().
763 // Hence, this addition can never overflow (as sig_digits is unsigned itself).
764 read_bits += rbits;
765 }
766 // NOTE: from earlier we know the exponent is well within the range of long, and read_bits
767 // cannot be larger than 113.
768 retval = scalbln(retval, static_cast<long>(m_mpfr._mpfr_exp) - static_cast<long>(read_bits));
769 return sgn() > 0 ? retval : -retval;
770 }
771
dispatch_get(real128 & x) const772 bool real::dispatch_get(real128 &x) const
773 {
774 x = static_cast<real128>(*this);
775 return true;
776 }
777
778 #endif
779
780 // Wrapper to apply the input unary MPFR function to this with
781 // MPFR_RNDN rounding mode. Returns a reference to this.
782 template <typename T>
self_mpfr_unary(T && f)783 real &real::self_mpfr_unary(T &&f)
784 {
785 std::forward<T>(f)(&m_mpfr, &m_mpfr, MPFR_RNDN);
786 return *this;
787 }
788
789 // In-place Gamma function.
gamma()790 real &real::gamma()
791 {
792 return self_mpfr_unary(::mpfr_gamma);
793 }
794
795 // In-place logarithm of the Gamma function.
lngamma()796 real &real::lngamma()
797 {
798 return self_mpfr_unary(::mpfr_lngamma);
799 }
800
801 // In-place logarithm of the absolute value of the Gamma function.
lgamma()802 real &real::lgamma()
803 {
804 detail::real_lgamma_wrapper(&m_mpfr, &m_mpfr, MPFR_RNDN);
805 return *this;
806 }
807
808 // In-place Digamma function.
digamma()809 real &real::digamma()
810 {
811 return self_mpfr_unary(::mpfr_digamma);
812 }
813
814 // In-place Bessel function of the first kind of order 0.
j0()815 real &real::j0()
816 {
817 return self_mpfr_unary(::mpfr_j0);
818 }
819
820 // In-place Bessel function of the first kind of order 1.
j1()821 real &real::j1()
822 {
823 return self_mpfr_unary(::mpfr_j1);
824 }
825
826 // In-place Bessel function of the second kind of order 0.
y0()827 real &real::y0()
828 {
829 return self_mpfr_unary(::mpfr_y0);
830 }
831
832 // In-place Bessel function of the second kind of order 1.
y1()833 real &real::y1()
834 {
835 return self_mpfr_unary(::mpfr_y1);
836 }
837
838 // In-place exponential integral.
eint()839 real &real::eint()
840 {
841 return self_mpfr_unary(::mpfr_eint);
842 }
843
844 // In-place dilogarithm.
li2()845 real &real::li2()
846 {
847 return self_mpfr_unary(detail::real_li2_wrapper);
848 }
849
850 // In-place Riemann Zeta function.
zeta()851 real &real::zeta()
852 {
853 return self_mpfr_unary(::mpfr_zeta);
854 }
855
856 // In-place error function.
erf()857 real &real::erf()
858 {
859 return self_mpfr_unary(::mpfr_erf);
860 }
861
862 // In-place complementary error function.
erfc()863 real &real::erfc()
864 {
865 return self_mpfr_unary(::mpfr_erfc);
866 }
867
868 // In-place Airy function.
ai()869 real &real::ai()
870 {
871 return self_mpfr_unary(::mpfr_ai);
872 }
873
874 // Negate in-place.
neg()875 real &real::neg()
876 {
877 return self_mpfr_unary(::mpfr_neg);
878 }
879
880 // In-place absolute value.
abs()881 real &real::abs()
882 {
883 return self_mpfr_unary(::mpfr_abs);
884 }
885
886 // Destructively set the precision.
set_prec(::mpfr_prec_t p)887 real &real::set_prec(::mpfr_prec_t p)
888 {
889 set_prec_impl<true>(p);
890 return *this;
891 }
892
893 // Set the precision maintaining the current value.
prec_round(::mpfr_prec_t p)894 real &real::prec_round(::mpfr_prec_t p)
895 {
896 prec_round_impl<true>(p);
897 return *this;
898 }
899
900 // Convert to string.
to_string(int base) const901 std::string real::to_string(int base) const
902 {
903 std::ostringstream oss;
904 oss.exceptions(std::ios_base::failbit | std::ios_base::badbit);
905 detail::mpfr_to_stream(&m_mpfr, oss, base);
906 return oss.str();
907 }
908
909 // In-place square root.
sqrt()910 real &real::sqrt()
911 {
912 return self_mpfr_unary(::mpfr_sqrt);
913 }
914
915 // In-place reciprocal square root.
rec_sqrt()916 real &real::rec_sqrt()
917 {
918 return self_mpfr_unary(::mpfr_rec_sqrt);
919 }
920
921 // In-place cubic root.
cbrt()922 real &real::cbrt()
923 {
924 return self_mpfr_unary(::mpfr_cbrt);
925 }
926
927 // In-place squaring.
sqr()928 real &real::sqr()
929 {
930 return self_mpfr_unary(::mpfr_sqr);
931 }
932
933 // In-place sine.
sin()934 real &real::sin()
935 {
936 return self_mpfr_unary(::mpfr_sin);
937 }
938
939 // In-place cosine.
cos()940 real &real::cos()
941 {
942 return self_mpfr_unary(::mpfr_cos);
943 }
944
945 // In-place tangent.
tan()946 real &real::tan()
947 {
948 return self_mpfr_unary(::mpfr_tan);
949 }
950
951 // In-place secant.
sec()952 real &real::sec()
953 {
954 return self_mpfr_unary(::mpfr_sec);
955 }
956
957 // In-place cosecant.
csc()958 real &real::csc()
959 {
960 return self_mpfr_unary(::mpfr_csc);
961 }
962
963 // In-place cotangent.
cot()964 real &real::cot()
965 {
966 return self_mpfr_unary(::mpfr_cot);
967 }
968
969 // In-place arccosine.
acos()970 real &real::acos()
971 {
972 return self_mpfr_unary(::mpfr_acos);
973 }
974
975 // In-place arcsine.
asin()976 real &real::asin()
977 {
978 return self_mpfr_unary(::mpfr_asin);
979 }
980
981 // In-place arctangent.
atan()982 real &real::atan()
983 {
984 return self_mpfr_unary(::mpfr_atan);
985 }
986
987 // In-place hyperbolic cosine.
cosh()988 real &real::cosh()
989 {
990 return self_mpfr_unary(::mpfr_cosh);
991 }
992
993 // In-place hyperbolic sine.
sinh()994 real &real::sinh()
995 {
996 return self_mpfr_unary(::mpfr_sinh);
997 }
998
999 // In-place hyperbolic tangent.
tanh()1000 real &real::tanh()
1001 {
1002 return self_mpfr_unary(::mpfr_tanh);
1003 }
1004
1005 // In-place hyperbolic secant.
sech()1006 real &real::sech()
1007 {
1008 return self_mpfr_unary(::mpfr_sech);
1009 }
1010
1011 // In-place hyperbolic cosecant.
csch()1012 real &real::csch()
1013 {
1014 return self_mpfr_unary(::mpfr_csch);
1015 }
1016
1017 // In-place hyperbolic cotangent.
coth()1018 real &real::coth()
1019 {
1020 return self_mpfr_unary(::mpfr_coth);
1021 }
1022
1023 // In-place inverse hyperbolic cosine.
acosh()1024 real &real::acosh()
1025 {
1026 return self_mpfr_unary(::mpfr_acosh);
1027 }
1028
1029 // In-place inverse hyperbolic sine.
asinh()1030 real &real::asinh()
1031 {
1032 return self_mpfr_unary(::mpfr_asinh);
1033 }
1034
1035 // In-place inverse hyperbolic tangent.
atanh()1036 real &real::atanh()
1037 {
1038 return self_mpfr_unary(::mpfr_atanh);
1039 }
1040
1041 // In-place exponential.
exp()1042 real &real::exp()
1043 {
1044 return self_mpfr_unary(::mpfr_exp);
1045 }
1046
1047 // In-place base-2 exponential.
exp2()1048 real &real::exp2()
1049 {
1050 return self_mpfr_unary(::mpfr_exp2);
1051 }
1052
1053 // In-place base-10 exponential.
exp10()1054 real &real::exp10()
1055 {
1056 return self_mpfr_unary(::mpfr_exp10);
1057 }
1058
1059 // In-place exponential minus 1.
expm1()1060 real &real::expm1()
1061 {
1062 return self_mpfr_unary(::mpfr_expm1);
1063 }
1064
1065 // In-place logarithm.
log()1066 real &real::log()
1067 {
1068 return self_mpfr_unary(::mpfr_log);
1069 }
1070
1071 // In-place base-2 logarithm.
log2()1072 real &real::log2()
1073 {
1074 return self_mpfr_unary(::mpfr_log2);
1075 }
1076
1077 // In-place base-10 logarithm.
log10()1078 real &real::log10()
1079 {
1080 return self_mpfr_unary(::mpfr_log10);
1081 }
1082
1083 // In-place augmented logarithm.
log1p()1084 real &real::log1p()
1085 {
1086 return self_mpfr_unary(::mpfr_log1p);
1087 }
1088
1089 // Check if the value is an integer.
integer_p() const1090 bool real::integer_p() const
1091 {
1092 return ::mpfr_integer_p(&m_mpfr) != 0;
1093 }
1094
1095 // In-place integer and remainder-related functions.
ceil()1096 real &real::ceil()
1097 {
1098 return self_mpfr_unary_nornd(detail::real_ceil_wrapper);
1099 }
1100
floor()1101 real &real::floor()
1102 {
1103 return self_mpfr_unary_nornd(detail::real_floor_wrapper);
1104 }
1105
round()1106 real &real::round()
1107 {
1108 return self_mpfr_unary_nornd(detail::real_round_wrapper);
1109 }
1110
1111 #if defined(MPPP_MPFR_HAVE_MPFR_ROUNDEVEN)
1112
roundeven()1113 real &real::roundeven()
1114 {
1115 return self_mpfr_unary_nornd(detail::real_roundeven_wrapper);
1116 }
1117
1118 #endif
1119
trunc()1120 real &real::trunc()
1121 {
1122 return self_mpfr_unary_nornd(detail::real_trunc_wrapper);
1123 }
1124
frac()1125 real &real::frac()
1126 {
1127 return self_mpfr_unary_nornd(detail::real_frac_wrapper);
1128 }
1129
1130 // Set to n*2**e.
set_ui_2exp(real & r,unsigned long n,::mpfr_exp_t e)1131 real &set_ui_2exp(real &r, unsigned long n, ::mpfr_exp_t e)
1132 {
1133 ::mpfr_set_ui_2exp(r._get_mpfr_t(), n, e, MPFR_RNDN);
1134 return r;
1135 }
1136
set_si_2exp(real & r,long n,::mpfr_exp_t e)1137 real &set_si_2exp(real &r, long n, ::mpfr_exp_t e)
1138 {
1139 ::mpfr_set_si_2exp(r._get_mpfr_t(), n, e, MPFR_RNDN);
1140 return r;
1141 }
1142
1143 // Implementation bits for in-place addition.
1144 namespace detail
1145 {
1146
dispatch_real_in_place_add_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1147 void dispatch_real_in_place_add_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1148 {
1149 auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_z(r, o, n, MPFR_RNDN); };
1150
1151 // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1152 // which is by definition valid because it is produced by an invocation of
1153 // real_deduce_precision() (which does clamping).
1154 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1155 }
1156
dispatch_real_in_place_add(real & a,bool n)1157 void dispatch_real_in_place_add(real &a, bool n)
1158 {
1159 auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1160
1161 mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1162 }
1163
dispatch_real_in_place_add_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1164 void dispatch_real_in_place_add_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1165 {
1166 auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_q(r, o, q, MPFR_RNDN); };
1167
1168 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1169 }
1170
1171 namespace
1172 {
1173
1174 template <typename T>
dispatch_real_in_place_add_fd_impl(real & a,const T & x)1175 inline void dispatch_real_in_place_add_fd_impl(real &a, const T &x)
1176 {
1177 // NOTE: the MPFR docs state that mpfr_add_d() assumes that
1178 // the radix of double is a power of 2. If we ever run into platforms
1179 // for which this is not true, we can add a compile-time dispatch
1180 // that uses the long double implementation instead.
1181 constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1182 static_assert(!(dradix & (dradix - 1)), "mpfr_add_d() requires the radix of the 'double' type to be a power of 2.");
1183
1184 auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1185
1186 mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1187 }
1188
1189 } // namespace
1190
dispatch_real_in_place_add(real & a,const float & x)1191 void dispatch_real_in_place_add(real &a, const float &x)
1192 {
1193 dispatch_real_in_place_add_fd_impl(a, x);
1194 }
1195
dispatch_real_in_place_add(real & a,const double & x)1196 void dispatch_real_in_place_add(real &a, const double &x)
1197 {
1198 dispatch_real_in_place_add_fd_impl(a, x);
1199 }
1200
1201 namespace
1202 {
1203
1204 template <typename T>
dispatch_real_in_place_add_generic_impl(real & a,const T & x)1205 inline void dispatch_real_in_place_add_generic_impl(real &a, const T &x)
1206 {
1207 MPPP_MAYBE_TLS real tmp;
1208 tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1209 tmp.set(x);
1210 dispatch_real_in_place_add(a, tmp);
1211 }
1212
1213 } // namespace
1214
dispatch_real_in_place_add(real & a,const long double & x)1215 void dispatch_real_in_place_add(real &a, const long double &x)
1216 {
1217 dispatch_real_in_place_add_generic_impl(a, x);
1218 }
1219
1220 #if defined(MPPP_WITH_QUADMATH)
1221
dispatch_real_in_place_add(real & a,const real128 & x)1222 void dispatch_real_in_place_add(real &a, const real128 &x)
1223 {
1224 dispatch_real_in_place_add_generic_impl(a, x);
1225 }
1226
1227 #endif
1228
1229 } // namespace detail
1230
operator ++(real & x)1231 real &operator++(real &x)
1232 {
1233 if (mppp_unlikely(x.get_prec() < detail::real_deduce_precision(1))) {
1234 x.prec_round(detail::real_deduce_precision(1));
1235 }
1236 ::mpfr_add_ui(x._get_mpfr_t(), x.get_mpfr_t(), 1ul, MPFR_RNDN);
1237 return x;
1238 }
1239
operator ++(real & x,int)1240 real operator++(real &x, int)
1241 {
1242 auto retval(x);
1243 ++x;
1244 return retval;
1245 }
1246
1247 // Implementation bits for in-place subtraction.
1248 namespace detail
1249 {
1250
dispatch_real_in_place_sub_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1251 void dispatch_real_in_place_sub_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1252 {
1253 auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_z(r, o, n, MPFR_RNDN); };
1254
1255 // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1256 // which is by definition valid because it is produced by an invocation of
1257 // real_deduce_precision() (which does clamping).
1258 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1259 }
1260
dispatch_real_in_place_sub(real & a,bool n)1261 void dispatch_real_in_place_sub(real &a, bool n)
1262 {
1263 auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1264
1265 mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1266 }
1267
dispatch_real_in_place_sub_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1268 void dispatch_real_in_place_sub_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1269 {
1270 auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_q(r, o, q, MPFR_RNDN); };
1271
1272 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1273 }
1274
1275 namespace
1276 {
1277
1278 template <typename T>
dispatch_real_in_place_sub_fd_impl(real & a,const T & x)1279 inline void dispatch_real_in_place_sub_fd_impl(real &a, const T &x)
1280 {
1281 // NOTE: the MPFR docs state that mpfr_sub_d() assumes that
1282 // the radix of double is a power of 2. If we ever run into platforms
1283 // for which this is not true, we can add a compile-time dispatch
1284 // that uses the long double implementation instead.
1285 constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1286 static_assert(!(dradix & (dradix - 1)), "mpfr_sub_d() requires the radix of the 'double' type to be a power of 2.");
1287
1288 auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1289
1290 mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1291 }
1292
1293 } // namespace
1294
dispatch_real_in_place_sub(real & a,const float & x)1295 void dispatch_real_in_place_sub(real &a, const float &x)
1296 {
1297 dispatch_real_in_place_sub_fd_impl(a, x);
1298 }
1299
dispatch_real_in_place_sub(real & a,const double & x)1300 void dispatch_real_in_place_sub(real &a, const double &x)
1301 {
1302 dispatch_real_in_place_sub_fd_impl(a, x);
1303 }
1304
1305 namespace
1306 {
1307
1308 template <typename T>
dispatch_real_in_place_sub_generic_impl(real & a,const T & x)1309 inline void dispatch_real_in_place_sub_generic_impl(real &a, const T &x)
1310 {
1311 MPPP_MAYBE_TLS real tmp;
1312 tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1313 tmp.set(x);
1314 dispatch_real_in_place_sub(a, tmp);
1315 }
1316
1317 } // namespace
1318
dispatch_real_in_place_sub(real & a,const long double & x)1319 void dispatch_real_in_place_sub(real &a, const long double &x)
1320 {
1321 dispatch_real_in_place_sub_generic_impl(a, x);
1322 }
1323
1324 #if defined(MPPP_WITH_QUADMATH)
1325
dispatch_real_in_place_sub(real & a,const real128 & x)1326 void dispatch_real_in_place_sub(real &a, const real128 &x)
1327 {
1328 dispatch_real_in_place_sub_generic_impl(a, x);
1329 }
1330
1331 #endif
1332
1333 } // namespace detail
1334
operator --(real & x)1335 real &operator--(real &x)
1336 {
1337 if (mppp_unlikely(x.get_prec() < detail::real_deduce_precision(1))) {
1338 x.prec_round(detail::real_deduce_precision(1));
1339 }
1340 ::mpfr_sub_ui(x._get_mpfr_t(), x.get_mpfr_t(), 1ul, MPFR_RNDN);
1341 return x;
1342 }
1343
operator --(real & x,int)1344 real operator--(real &x, int)
1345 {
1346 auto retval(x);
1347 --x;
1348 return retval;
1349 }
1350
1351 // Implementation bits for in-place multiplication.
1352 namespace detail
1353 {
1354
dispatch_real_in_place_mul_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1355 void dispatch_real_in_place_mul_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1356 {
1357 auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_mul_z(r, o, n, MPFR_RNDN); };
1358
1359 // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1360 // which is by definition valid because it is produced by an invocation of
1361 // real_deduce_precision() (which does clamping).
1362 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1363 }
1364
dispatch_real_in_place_mul(real & a,bool n)1365 void dispatch_real_in_place_mul(real &a, bool n)
1366 {
1367 auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { mpfr_mul_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1368
1369 mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1370 }
1371
dispatch_real_in_place_mul_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1372 void dispatch_real_in_place_mul_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1373 {
1374 auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_mul_q(r, o, q, MPFR_RNDN); };
1375
1376 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1377 }
1378
1379 namespace
1380 {
1381
1382 template <typename T>
dispatch_real_in_place_mul_fd_impl(real & a,const T & x)1383 inline void dispatch_real_in_place_mul_fd_impl(real &a, const T &x)
1384 {
1385 // NOTE: the MPFR docs state that mpfr_mul_d() assumes that
1386 // the radix of double is a power of 2. If we ever run into platforms
1387 // for which this is not true, we can add a compile-time dispatch
1388 // that uses the long double implementation instead.
1389 constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1390 static_assert(!(dradix & (dradix - 1)), "mpfr_mul_d() requires the radix of the 'double' type to be a power of 2.");
1391
1392 auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_mul_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1393
1394 mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1395 }
1396
1397 } // namespace
1398
dispatch_real_in_place_mul(real & a,const float & x)1399 void dispatch_real_in_place_mul(real &a, const float &x)
1400 {
1401 dispatch_real_in_place_mul_fd_impl(a, x);
1402 }
1403
dispatch_real_in_place_mul(real & a,const double & x)1404 void dispatch_real_in_place_mul(real &a, const double &x)
1405 {
1406 dispatch_real_in_place_mul_fd_impl(a, x);
1407 }
1408
1409 namespace
1410 {
1411
1412 template <typename T>
dispatch_real_in_place_mul_generic_impl(real & a,const T & x)1413 inline void dispatch_real_in_place_mul_generic_impl(real &a, const T &x)
1414 {
1415 MPPP_MAYBE_TLS real tmp;
1416 tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1417 tmp.set(x);
1418 dispatch_real_in_place_mul(a, tmp);
1419 }
1420
1421 } // namespace
1422
dispatch_real_in_place_mul(real & a,const long double & x)1423 void dispatch_real_in_place_mul(real &a, const long double &x)
1424 {
1425 dispatch_real_in_place_mul_generic_impl(a, x);
1426 }
1427
1428 #if defined(MPPP_WITH_QUADMATH)
1429
dispatch_real_in_place_mul(real & a,const real128 & x)1430 void dispatch_real_in_place_mul(real &a, const real128 &x)
1431 {
1432 dispatch_real_in_place_mul_generic_impl(a, x);
1433 }
1434
1435 #endif
1436
1437 } // namespace detail
1438
1439 // Implementation bits for in-place division.
1440 namespace detail
1441 {
1442
dispatch_real_in_place_div_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1443 void dispatch_real_in_place_div_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1444 {
1445 auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_div_z(r, o, n, MPFR_RNDN); };
1446
1447 // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1448 // which is by definition valid because it is produced by an invocation of
1449 // real_deduce_precision() (which does clamping).
1450 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1451 }
1452
dispatch_real_in_place_div(real & a,bool n)1453 void dispatch_real_in_place_div(real &a, bool n)
1454 {
1455 auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { mpfr_div_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1456
1457 mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1458 }
1459
dispatch_real_in_place_div_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1460 void dispatch_real_in_place_div_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1461 {
1462 auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_div_q(r, o, q, MPFR_RNDN); };
1463
1464 mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1465 }
1466
1467 namespace
1468 {
1469
1470 template <typename T>
dispatch_real_in_place_div_fd_impl(real & a,const T & x)1471 inline void dispatch_real_in_place_div_fd_impl(real &a, const T &x)
1472 {
1473 // NOTE: the MPFR docs state that mpfr_div_d() assumes that
1474 // the radix of double is a power of 2. If we ever run into platforms
1475 // for which this is not true, we can add a compile-time dispatch
1476 // that uses the long double implementation instead.
1477 constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1478 static_assert(!(dradix & (dradix - 1)), "mpfr_div_d() requires the radix of the 'double' type to be a power of 2.");
1479
1480 auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_div_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1481
1482 mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1483 }
1484
1485 } // namespace
1486
dispatch_real_in_place_div(real & a,const float & x)1487 void dispatch_real_in_place_div(real &a, const float &x)
1488 {
1489 dispatch_real_in_place_div_fd_impl(a, x);
1490 }
1491
dispatch_real_in_place_div(real & a,const double & x)1492 void dispatch_real_in_place_div(real &a, const double &x)
1493 {
1494 dispatch_real_in_place_div_fd_impl(a, x);
1495 }
1496
1497 namespace
1498 {
1499
1500 template <typename T>
dispatch_real_in_place_div_generic_impl(real & a,const T & x)1501 inline void dispatch_real_in_place_div_generic_impl(real &a, const T &x)
1502 {
1503 MPPP_MAYBE_TLS real tmp;
1504 tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1505 tmp.set(x);
1506 dispatch_real_in_place_div(a, tmp);
1507 }
1508
1509 } // namespace
1510
dispatch_real_in_place_div(real & a,const long double & x)1511 void dispatch_real_in_place_div(real &a, const long double &x)
1512 {
1513 dispatch_real_in_place_div_generic_impl(a, x);
1514 }
1515
1516 #if defined(MPPP_WITH_QUADMATH)
1517
dispatch_real_in_place_div(real & a,const real128 & x)1518 void dispatch_real_in_place_div(real &a, const real128 &x)
1519 {
1520 dispatch_real_in_place_div_generic_impl(a, x);
1521 }
1522
1523 #endif
1524
1525 } // namespace detail
1526
1527 // Implementation details for equality.
1528 namespace detail
1529 {
1530
dispatch_real_equality(const real & x,const real & y)1531 bool dispatch_real_equality(const real &x, const real &y)
1532 {
1533 return ::mpfr_equal_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1534 }
1535
dispatch_real_equality_integer_impl(const real & r,const::mpz_t n)1536 bool dispatch_real_equality_integer_impl(const real &r, const ::mpz_t n)
1537 {
1538 if (r.nan_p()) {
1539 return false;
1540 } else {
1541 return ::mpfr_cmp_z(r.get_mpfr_t(), n) == 0;
1542 }
1543 }
1544
dispatch_real_equality(const real & r,bool b)1545 bool dispatch_real_equality(const real &r, bool b)
1546 {
1547 if (r.nan_p()) {
1548 return false;
1549 } else {
1550 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) == 0;
1551 }
1552 }
1553
dispatch_real_equality_rational_impl(const real & r,const::mpq_t q)1554 bool dispatch_real_equality_rational_impl(const real &r, const ::mpq_t q)
1555 {
1556 if (r.nan_p()) {
1557 return false;
1558 } else {
1559 return ::mpfr_cmp_q(r.get_mpfr_t(), q) == 0;
1560 }
1561 }
1562
dispatch_real_equality(const real & r,const float & x)1563 bool dispatch_real_equality(const real &r, const float &x)
1564 {
1565 if (r.nan_p() || std::isnan(x)) {
1566 return false;
1567 } else {
1568 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) == 0;
1569 }
1570 }
1571
dispatch_real_equality(const real & r,const double & x)1572 bool dispatch_real_equality(const real &r, const double &x)
1573 {
1574 if (r.nan_p() || std::isnan(x)) {
1575 return false;
1576 } else {
1577 return ::mpfr_cmp_d(r.get_mpfr_t(), x) == 0;
1578 }
1579 }
1580
dispatch_real_equality(const real & r,const long double & x)1581 bool dispatch_real_equality(const real &r, const long double &x)
1582 {
1583 if (r.nan_p() || std::isnan(x)) {
1584 return false;
1585 } else {
1586 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) == 0;
1587 }
1588 }
1589
1590 #if defined(MPPP_WITH_QUADMATH)
1591
dispatch_real_equality(const real & r1,const real128 & r2)1592 bool dispatch_real_equality(const real &r1, const real128 &r2)
1593 {
1594 // NOTE: straight assignment here is fine: tmp
1595 // will represent r2 exactly.
1596 MPPP_MAYBE_TLS real tmp;
1597 tmp = r2;
1598
1599 return dispatch_real_equality(r1, tmp);
1600 }
1601
1602 #endif
1603
1604 } // namespace detail
1605
1606 // Implementation details for gt.
1607 namespace detail
1608 {
1609
dispatch_real_gt(const real & x,const real & y)1610 bool dispatch_real_gt(const real &x, const real &y)
1611 {
1612 return ::mpfr_greater_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1613 }
1614
dispatch_real_gt_integer_impl(const real & r,const::mpz_t n)1615 bool dispatch_real_gt_integer_impl(const real &r, const ::mpz_t n)
1616 {
1617 if (r.nan_p()) {
1618 return false;
1619 } else {
1620 return ::mpfr_cmp_z(r.get_mpfr_t(), n) > 0;
1621 }
1622 }
1623
dispatch_real_gt_integer_impl(const::mpz_t n,const real & r)1624 bool dispatch_real_gt_integer_impl(const ::mpz_t n, const real &r)
1625 {
1626 if (r.nan_p()) {
1627 return false;
1628 } else {
1629 return ::mpfr_cmp_z(r.get_mpfr_t(), n) < 0;
1630 }
1631 }
1632
dispatch_real_gt(const real & r,bool b)1633 bool dispatch_real_gt(const real &r, bool b)
1634 {
1635 if (r.nan_p()) {
1636 return false;
1637 } else {
1638 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) > 0;
1639 }
1640 }
1641
dispatch_real_gt(bool b,const real & r)1642 bool dispatch_real_gt(bool b, const real &r)
1643 {
1644 if (r.nan_p()) {
1645 return false;
1646 } else {
1647 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) < 0;
1648 }
1649 }
1650
dispatch_real_gt_rational_impl(const real & r,const::mpq_t q)1651 bool dispatch_real_gt_rational_impl(const real &r, const ::mpq_t q)
1652 {
1653 if (r.nan_p()) {
1654 return false;
1655 } else {
1656 return ::mpfr_cmp_q(r.get_mpfr_t(), q) > 0;
1657 }
1658 }
1659
dispatch_real_gt_rational_impl(const::mpq_t q,const real & r)1660 bool dispatch_real_gt_rational_impl(const ::mpq_t q, const real &r)
1661 {
1662 if (r.nan_p()) {
1663 return false;
1664 } else {
1665 return ::mpfr_cmp_q(r.get_mpfr_t(), q) < 0;
1666 }
1667 }
1668
dispatch_real_gt(const real & r,const float & x)1669 bool dispatch_real_gt(const real &r, const float &x)
1670 {
1671 if (r.nan_p() || std::isnan(x)) {
1672 return false;
1673 } else {
1674 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) > 0;
1675 }
1676 }
1677
dispatch_real_gt(const real & r,const double & x)1678 bool dispatch_real_gt(const real &r, const double &x)
1679 {
1680 if (r.nan_p() || std::isnan(x)) {
1681 return false;
1682 } else {
1683 return ::mpfr_cmp_d(r.get_mpfr_t(), x) > 0;
1684 }
1685 }
1686
dispatch_real_gt(const real & r,const long double & x)1687 bool dispatch_real_gt(const real &r, const long double &x)
1688 {
1689 if (r.nan_p() || std::isnan(x)) {
1690 return false;
1691 } else {
1692 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) > 0;
1693 }
1694 }
1695
dispatch_real_gt(const float & x,const real & r)1696 bool dispatch_real_gt(const float &x, const real &r)
1697 {
1698 if (r.nan_p() || std::isnan(x)) {
1699 return false;
1700 } else {
1701 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) < 0;
1702 }
1703 }
1704
dispatch_real_gt(const double & x,const real & r)1705 bool dispatch_real_gt(const double &x, const real &r)
1706 {
1707 if (r.nan_p() || std::isnan(x)) {
1708 return false;
1709 } else {
1710 return ::mpfr_cmp_d(r.get_mpfr_t(), x) < 0;
1711 }
1712 }
1713
dispatch_real_gt(const long double & x,const real & r)1714 bool dispatch_real_gt(const long double &x, const real &r)
1715 {
1716 if (r.nan_p() || std::isnan(x)) {
1717 return false;
1718 } else {
1719 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) < 0;
1720 }
1721 }
1722
1723 #if defined(MPPP_WITH_QUADMATH)
1724
dispatch_real_gt(const real & r1,const real128 & r2)1725 bool dispatch_real_gt(const real &r1, const real128 &r2)
1726 {
1727 // NOTE: straight assignment here is fine: tmp
1728 // will represent r2 exactly.
1729 MPPP_MAYBE_TLS real tmp;
1730 tmp = r2;
1731
1732 return dispatch_real_gt(r1, tmp);
1733 }
1734
dispatch_real_gt(const real128 & r1,const real & r2)1735 bool dispatch_real_gt(const real128 &r1, const real &r2)
1736 {
1737 MPPP_MAYBE_TLS real tmp;
1738 tmp = r1;
1739
1740 return dispatch_real_gt(tmp, r2);
1741 }
1742
1743 #endif
1744
1745 } // namespace detail
1746
1747 // Implementation details for gte.
1748 namespace detail
1749 {
1750
dispatch_real_gte(const real & x,const real & y)1751 bool dispatch_real_gte(const real &x, const real &y)
1752 {
1753 return ::mpfr_greaterequal_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1754 }
1755
dispatch_real_gte_integer_impl(const real & r,const::mpz_t n)1756 bool dispatch_real_gte_integer_impl(const real &r, const ::mpz_t n)
1757 {
1758 if (r.nan_p()) {
1759 return false;
1760 } else {
1761 return ::mpfr_cmp_z(r.get_mpfr_t(), n) >= 0;
1762 }
1763 }
1764
dispatch_real_gte_integer_impl(const::mpz_t n,const real & r)1765 bool dispatch_real_gte_integer_impl(const ::mpz_t n, const real &r)
1766 {
1767 if (r.nan_p()) {
1768 return false;
1769 } else {
1770 return ::mpfr_cmp_z(r.get_mpfr_t(), n) <= 0;
1771 }
1772 }
1773
dispatch_real_gte(const real & r,bool b)1774 bool dispatch_real_gte(const real &r, bool b)
1775 {
1776 if (r.nan_p()) {
1777 return false;
1778 } else {
1779 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) >= 0;
1780 }
1781 }
1782
dispatch_real_gte(bool b,const real & r)1783 bool dispatch_real_gte(bool b, const real &r)
1784 {
1785 if (r.nan_p()) {
1786 return false;
1787 } else {
1788 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) <= 0;
1789 }
1790 }
1791
dispatch_real_gte_rational_impl(const real & r,const::mpq_t q)1792 bool dispatch_real_gte_rational_impl(const real &r, const ::mpq_t q)
1793 {
1794 if (r.nan_p()) {
1795 return false;
1796 } else {
1797 return ::mpfr_cmp_q(r.get_mpfr_t(), q) >= 0;
1798 }
1799 }
1800
dispatch_real_gte_rational_impl(const::mpq_t q,const real & r)1801 bool dispatch_real_gte_rational_impl(const ::mpq_t q, const real &r)
1802 {
1803 if (r.nan_p()) {
1804 return false;
1805 } else {
1806 return ::mpfr_cmp_q(r.get_mpfr_t(), q) <= 0;
1807 }
1808 }
1809
dispatch_real_gte(const real & r,const float & x)1810 bool dispatch_real_gte(const real &r, const float &x)
1811 {
1812 if (r.nan_p() || std::isnan(x)) {
1813 return false;
1814 } else {
1815 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) >= 0;
1816 }
1817 }
1818
dispatch_real_gte(const real & r,const double & x)1819 bool dispatch_real_gte(const real &r, const double &x)
1820 {
1821 if (r.nan_p() || std::isnan(x)) {
1822 return false;
1823 } else {
1824 return ::mpfr_cmp_d(r.get_mpfr_t(), x) >= 0;
1825 }
1826 }
1827
dispatch_real_gte(const real & r,const long double & x)1828 bool dispatch_real_gte(const real &r, const long double &x)
1829 {
1830 if (r.nan_p() || std::isnan(x)) {
1831 return false;
1832 } else {
1833 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) >= 0;
1834 }
1835 }
1836
dispatch_real_gte(const float & x,const real & r)1837 bool dispatch_real_gte(const float &x, const real &r)
1838 {
1839 if (r.nan_p() || std::isnan(x)) {
1840 return false;
1841 } else {
1842 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) <= 0;
1843 }
1844 }
1845
dispatch_real_gte(const double & x,const real & r)1846 bool dispatch_real_gte(const double &x, const real &r)
1847 {
1848 if (r.nan_p() || std::isnan(x)) {
1849 return false;
1850 } else {
1851 return ::mpfr_cmp_d(r.get_mpfr_t(), x) <= 0;
1852 }
1853 }
1854
dispatch_real_gte(const long double & x,const real & r)1855 bool dispatch_real_gte(const long double &x, const real &r)
1856 {
1857 if (r.nan_p() || std::isnan(x)) {
1858 return false;
1859 } else {
1860 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) <= 0;
1861 }
1862 }
1863
1864 #if defined(MPPP_WITH_QUADMATH)
1865
dispatch_real_gte(const real & r1,const real128 & r2)1866 bool dispatch_real_gte(const real &r1, const real128 &r2)
1867 {
1868 // NOTE: straight assignment here is fine: tmp
1869 // will represent r2 exactly.
1870 MPPP_MAYBE_TLS real tmp;
1871 tmp = r2;
1872
1873 return dispatch_real_gte(r1, tmp);
1874 }
1875
dispatch_real_gte(const real128 & r1,const real & r2)1876 bool dispatch_real_gte(const real128 &r1, const real &r2)
1877 {
1878 MPPP_MAYBE_TLS real tmp;
1879 tmp = r1;
1880
1881 return dispatch_real_gte(tmp, r2);
1882 }
1883
1884 #endif
1885
1886 } // namespace detail
1887
1888 // Implementation details for lt.
1889 namespace detail
1890 {
1891
dispatch_real_lt(const real & x,const real & y)1892 bool dispatch_real_lt(const real &x, const real &y)
1893 {
1894 return ::mpfr_less_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1895 }
1896
dispatch_real_lt_integer_impl(const real & r,const::mpz_t n)1897 bool dispatch_real_lt_integer_impl(const real &r, const ::mpz_t n)
1898 {
1899 if (r.nan_p()) {
1900 return false;
1901 } else {
1902 return ::mpfr_cmp_z(r.get_mpfr_t(), n) < 0;
1903 }
1904 }
1905
dispatch_real_lt_integer_impl(const::mpz_t n,const real & r)1906 bool dispatch_real_lt_integer_impl(const ::mpz_t n, const real &r)
1907 {
1908 if (r.nan_p()) {
1909 return false;
1910 } else {
1911 return ::mpfr_cmp_z(r.get_mpfr_t(), n) > 0;
1912 }
1913 }
1914
dispatch_real_lt(const real & r,bool b)1915 bool dispatch_real_lt(const real &r, bool b)
1916 {
1917 if (r.nan_p()) {
1918 return false;
1919 } else {
1920 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) < 0;
1921 }
1922 }
1923
dispatch_real_lt(bool b,const real & r)1924 bool dispatch_real_lt(bool b, const real &r)
1925 {
1926 if (r.nan_p()) {
1927 return false;
1928 } else {
1929 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) > 0;
1930 }
1931 }
1932
dispatch_real_lt_rational_impl(const real & r,const::mpq_t q)1933 bool dispatch_real_lt_rational_impl(const real &r, const ::mpq_t q)
1934 {
1935 if (r.nan_p()) {
1936 return false;
1937 } else {
1938 return ::mpfr_cmp_q(r.get_mpfr_t(), q) < 0;
1939 }
1940 }
1941
dispatch_real_lt_rational_impl(const::mpq_t q,const real & r)1942 bool dispatch_real_lt_rational_impl(const ::mpq_t q, const real &r)
1943 {
1944 if (r.nan_p()) {
1945 return false;
1946 } else {
1947 return ::mpfr_cmp_q(r.get_mpfr_t(), q) > 0;
1948 }
1949 }
1950
dispatch_real_lt(const real & r,const float & x)1951 bool dispatch_real_lt(const real &r, const float &x)
1952 {
1953 if (r.nan_p() || std::isnan(x)) {
1954 return false;
1955 } else {
1956 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) < 0;
1957 }
1958 }
1959
dispatch_real_lt(const real & r,const double & x)1960 bool dispatch_real_lt(const real &r, const double &x)
1961 {
1962 if (r.nan_p() || std::isnan(x)) {
1963 return false;
1964 } else {
1965 return ::mpfr_cmp_d(r.get_mpfr_t(), x) < 0;
1966 }
1967 }
1968
dispatch_real_lt(const real & r,const long double & x)1969 bool dispatch_real_lt(const real &r, const long double &x)
1970 {
1971 if (r.nan_p() || std::isnan(x)) {
1972 return false;
1973 } else {
1974 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) < 0;
1975 }
1976 }
1977
dispatch_real_lt(const float & x,const real & r)1978 bool dispatch_real_lt(const float &x, const real &r)
1979 {
1980 if (r.nan_p() || std::isnan(x)) {
1981 return false;
1982 } else {
1983 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) > 0;
1984 }
1985 }
1986
dispatch_real_lt(const double & x,const real & r)1987 bool dispatch_real_lt(const double &x, const real &r)
1988 {
1989 if (r.nan_p() || std::isnan(x)) {
1990 return false;
1991 } else {
1992 return ::mpfr_cmp_d(r.get_mpfr_t(), x) > 0;
1993 }
1994 }
1995
dispatch_real_lt(const long double & x,const real & r)1996 bool dispatch_real_lt(const long double &x, const real &r)
1997 {
1998 if (r.nan_p() || std::isnan(x)) {
1999 return false;
2000 } else {
2001 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) > 0;
2002 }
2003 }
2004
2005 #if defined(MPPP_WITH_QUADMATH)
2006
dispatch_real_lt(const real & r1,const real128 & r2)2007 bool dispatch_real_lt(const real &r1, const real128 &r2)
2008 {
2009 // NOTE: straight assignment here is fine: tmp
2010 // will represent r2 exactly.
2011 MPPP_MAYBE_TLS real tmp;
2012 tmp = r2;
2013
2014 return dispatch_real_lt(r1, tmp);
2015 }
2016
dispatch_real_lt(const real128 & r1,const real & r2)2017 bool dispatch_real_lt(const real128 &r1, const real &r2)
2018 {
2019 MPPP_MAYBE_TLS real tmp;
2020 tmp = r1;
2021
2022 return dispatch_real_lt(tmp, r2);
2023 }
2024
2025 #endif
2026
2027 } // namespace detail
2028
2029 // Implementation details for lte.
2030 namespace detail
2031 {
2032
dispatch_real_lte(const real & x,const real & y)2033 bool dispatch_real_lte(const real &x, const real &y)
2034 {
2035 return ::mpfr_lessequal_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
2036 }
2037
dispatch_real_lte_integer_impl(const real & r,const::mpz_t n)2038 bool dispatch_real_lte_integer_impl(const real &r, const ::mpz_t n)
2039 {
2040 if (r.nan_p()) {
2041 return false;
2042 } else {
2043 return ::mpfr_cmp_z(r.get_mpfr_t(), n) <= 0;
2044 }
2045 }
2046
dispatch_real_lte_integer_impl(const::mpz_t n,const real & r)2047 bool dispatch_real_lte_integer_impl(const ::mpz_t n, const real &r)
2048 {
2049 if (r.nan_p()) {
2050 return false;
2051 } else {
2052 return ::mpfr_cmp_z(r.get_mpfr_t(), n) >= 0;
2053 }
2054 }
2055
dispatch_real_lte(const real & r,bool b)2056 bool dispatch_real_lte(const real &r, bool b)
2057 {
2058 if (r.nan_p()) {
2059 return false;
2060 } else {
2061 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) <= 0;
2062 }
2063 }
2064
dispatch_real_lte(bool b,const real & r)2065 bool dispatch_real_lte(bool b, const real &r)
2066 {
2067 if (r.nan_p()) {
2068 return false;
2069 } else {
2070 return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) >= 0;
2071 }
2072 }
2073
dispatch_real_lte_rational_impl(const real & r,const::mpq_t q)2074 bool dispatch_real_lte_rational_impl(const real &r, const ::mpq_t q)
2075 {
2076 if (r.nan_p()) {
2077 return false;
2078 } else {
2079 return ::mpfr_cmp_q(r.get_mpfr_t(), q) <= 0;
2080 }
2081 }
2082
dispatch_real_lte_rational_impl(const::mpq_t q,const real & r)2083 bool dispatch_real_lte_rational_impl(const ::mpq_t q, const real &r)
2084 {
2085 if (r.nan_p()) {
2086 return false;
2087 } else {
2088 return ::mpfr_cmp_q(r.get_mpfr_t(), q) >= 0;
2089 }
2090 }
2091
dispatch_real_lte(const real & r,const float & x)2092 bool dispatch_real_lte(const real &r, const float &x)
2093 {
2094 if (r.nan_p() || std::isnan(x)) {
2095 return false;
2096 } else {
2097 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) <= 0;
2098 }
2099 }
2100
dispatch_real_lte(const real & r,const double & x)2101 bool dispatch_real_lte(const real &r, const double &x)
2102 {
2103 if (r.nan_p() || std::isnan(x)) {
2104 return false;
2105 } else {
2106 return ::mpfr_cmp_d(r.get_mpfr_t(), x) <= 0;
2107 }
2108 }
2109
dispatch_real_lte(const real & r,const long double & x)2110 bool dispatch_real_lte(const real &r, const long double &x)
2111 {
2112 if (r.nan_p() || std::isnan(x)) {
2113 return false;
2114 } else {
2115 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) <= 0;
2116 }
2117 }
2118
dispatch_real_lte(const float & x,const real & r)2119 bool dispatch_real_lte(const float &x, const real &r)
2120 {
2121 if (r.nan_p() || std::isnan(x)) {
2122 return false;
2123 } else {
2124 return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) >= 0;
2125 }
2126 }
2127
dispatch_real_lte(const double & x,const real & r)2128 bool dispatch_real_lte(const double &x, const real &r)
2129 {
2130 if (r.nan_p() || std::isnan(x)) {
2131 return false;
2132 } else {
2133 return ::mpfr_cmp_d(r.get_mpfr_t(), x) >= 0;
2134 }
2135 }
2136
dispatch_real_lte(const long double & x,const real & r)2137 bool dispatch_real_lte(const long double &x, const real &r)
2138 {
2139 if (r.nan_p() || std::isnan(x)) {
2140 return false;
2141 } else {
2142 return ::mpfr_cmp_ld(r.get_mpfr_t(), x) >= 0;
2143 }
2144 }
2145
2146 #if defined(MPPP_WITH_QUADMATH)
2147
dispatch_real_lte(const real & r1,const real128 & r2)2148 bool dispatch_real_lte(const real &r1, const real128 &r2)
2149 {
2150 // NOTE: straight assignment here is fine: tmp
2151 // will represent r2 exactly.
2152 MPPP_MAYBE_TLS real tmp;
2153 tmp = r2;
2154
2155 return dispatch_real_lte(r1, tmp);
2156 }
2157
dispatch_real_lte(const real128 & r1,const real & r2)2158 bool dispatch_real_lte(const real128 &r1, const real &r2)
2159 {
2160 MPPP_MAYBE_TLS real tmp;
2161 tmp = r1;
2162
2163 return dispatch_real_lte(tmp, r2);
2164 }
2165
2166 #endif
2167
2168 } // namespace detail
2169
2170 // Three-way comparison.
cmp(const real & a,const real & b)2171 int cmp(const real &a, const real &b)
2172 {
2173 ::mpfr_clear_erangeflag();
2174 auto retval = mpfr_cmp(a.get_mpfr_t(), b.get_mpfr_t());
2175 if (mppp_unlikely(::mpfr_erangeflag_p())) {
2176 ::mpfr_clear_erangeflag();
2177 throw std::domain_error("Cannot compare two reals if at least one of them is NaN");
2178 }
2179 return retval;
2180 }
2181
2182 // Three-way comparison of absolute values.
cmpabs(const real & a,const real & b)2183 int cmpabs(const real &a, const real &b)
2184 {
2185 ::mpfr_clear_erangeflag();
2186 auto retval = ::mpfr_cmpabs(a.get_mpfr_t(), b.get_mpfr_t());
2187 if (mppp_unlikely(::mpfr_erangeflag_p())) {
2188 ::mpfr_clear_erangeflag();
2189 throw std::domain_error("Cannot compare the absolute values of two reals if at least one of them is NaN");
2190 }
2191 return retval;
2192 }
2193
2194 // Comparison with n*2**e.
cmp_ui_2exp(const real & x,unsigned long n,::mpfr_exp_t e)2195 int cmp_ui_2exp(const real &x, unsigned long n, ::mpfr_exp_t e)
2196 {
2197 ::mpfr_clear_erangeflag();
2198 auto retval = ::mpfr_cmp_ui_2exp(x.get_mpfr_t(), n, e);
2199 if (mppp_unlikely(::mpfr_erangeflag_p())) {
2200 ::mpfr_clear_erangeflag();
2201 throw std::domain_error("Cannot compare a real NaN to an integral multiple of a power of 2");
2202 }
2203 return retval;
2204 }
2205
cmp_si_2exp(const real & x,long n,::mpfr_exp_t e)2206 int cmp_si_2exp(const real &x, long n, ::mpfr_exp_t e)
2207 {
2208 ::mpfr_clear_erangeflag();
2209 auto retval = ::mpfr_cmp_si_2exp(x.get_mpfr_t(), n, e);
2210 if (mppp_unlikely(::mpfr_erangeflag_p())) {
2211 ::mpfr_clear_erangeflag();
2212 throw std::domain_error("Cannot compare a real NaN to an integral multiple of a power of 2");
2213 }
2214 return retval;
2215 }
2216
2217 // Equality predicate with special NaN handling.
real_equal_to(const real & a,const real & b)2218 bool real_equal_to(const real &a, const real &b)
2219 {
2220 const bool a_nan = a.nan_p(), b_nan = b.nan_p();
2221 return (!a_nan && !b_nan) ? (::mpfr_equal_p(a.get_mpfr_t(), b.get_mpfr_t()) != 0) : (a_nan && b_nan);
2222 }
2223
2224 // Less-than predicate with special NaN/moved handling.
real_lt(const real & a,const real & b)2225 bool real_lt(const real &a, const real &b)
2226 {
2227 if (!a.is_valid()) {
2228 // a is moved-from, consider it the largest possible value.
2229 return false;
2230 }
2231 if (!b.is_valid()) {
2232 // a is not moved-from, b is. a is smaller.
2233 return true;
2234 }
2235 const bool a_nan = a.nan_p();
2236 return (!a_nan && !b.nan_p()) ? (::mpfr_less_p(a.get_mpfr_t(), b.get_mpfr_t()) != 0) : !a_nan;
2237 }
2238
2239 // Greater-than predicate with special NaN/moved handling.
real_gt(const real & a,const real & b)2240 bool real_gt(const real &a, const real &b)
2241 {
2242 if (!b.is_valid()) {
2243 // b is moved-from, nothing can be bigger.
2244 return false;
2245 }
2246 if (!a.is_valid()) {
2247 // b is not moved-from, a is. a is bigger.
2248 return true;
2249 }
2250 const bool b_nan = b.nan_p();
2251 return (!a.nan_p() && !b_nan) ? (::mpfr_greater_p(a.get_mpfr_t(), b.get_mpfr_t()) != 0) : !b_nan;
2252 }
2253
2254 // Output stream operator.
operator <<(std::ostream & os,const real & r)2255 std::ostream &operator<<(std::ostream &os, const real &r)
2256 {
2257 // Get the stream width.
2258 const auto width = os.width();
2259
2260 // Fetch the stream's flags.
2261 const auto flags = os.flags();
2262
2263 // Check if we are using scientific, fixed or both.
2264 const auto scientific = (flags & std::ios_base::scientific) != 0;
2265 const auto fixed = (flags & std::ios_base::fixed) != 0;
2266 const auto hexfloat = scientific && fixed;
2267
2268 // Force decimal point character?
2269 const auto showpoint = (flags & std::ios_base::showpoint) != 0;
2270
2271 // Force the plus sign?
2272 const bool with_plus = (flags & std::ios_base::showpos) != 0;
2273
2274 // Uppercase?
2275 const bool uppercase = (flags & std::ios_base::uppercase) != 0;
2276
2277 // Fetch the precision too.
2278 auto precision = os.precision();
2279 // NOTE: if the precision is negative, reset it
2280 // to the default value of 6.
2281 if (precision < 0) {
2282 precision = 6;
2283 }
2284
2285 // Put together the format string.
2286 std::ostringstream oss;
2287 oss.exceptions(std::ios_base::failbit | std::ios_base::badbit);
2288 oss.imbue(std::locale::classic());
2289
2290 oss << '%';
2291
2292 if (showpoint) {
2293 oss << '#';
2294 }
2295
2296 if (with_plus) {
2297 oss << '+';
2298 }
2299
2300 if (hexfloat) {
2301 // NOTE: in hexfloat mode, we want to ignore the precision
2302 // setting in the stream object and print the exact representation:
2303 // https://en.cppreference.com/w/cpp/locale/num_put/put
2304 oss << 'R';
2305 } else {
2306 oss << '.' << precision << 'R';
2307 }
2308
2309 if (hexfloat) {
2310 oss << (uppercase ? 'A' : 'a');
2311 } else if (scientific) {
2312 oss << (uppercase ? 'E' : 'e');
2313 } else if (fixed) {
2314 // NOTE: in fixed format, the uppercase
2315 // setting is ignored for floating-point types:
2316 // https://en.cppreference.com/w/cpp/locale/num_put/put
2317 oss << 'f';
2318 } else {
2319 oss << (uppercase ? 'G' : 'g');
2320 }
2321
2322 const auto fmt_str = oss.str();
2323
2324 // Print out via the MPFR printf() function.
2325 char *out_str = nullptr;
2326 const auto ret = ::mpfr_asprintf(&out_str, fmt_str.c_str(), r.get_mpfr_t());
2327 if (ret == -1) {
2328 // LCOV_EXCL_START
2329 // NOTE: the MPFR docs state that if printf() returns -1, the errno variable and erange
2330 // flag are set. Let's clear them out before throwing.
2331 errno = 0;
2332 ::mpfr_clear_erangeflag();
2333
2334 throw std::invalid_argument("The mpfr_asprintf() function returned the error code -1");
2335 // LCOV_EXCL_STOP
2336 }
2337
2338 // printf() was successful, let's set up the RAII machinery to automatically
2339 // clear out_str before anything else.
2340 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
2341 struct out_str_cleaner {
2342 ~out_str_cleaner()
2343 {
2344 ::mpfr_free_str(ptr);
2345 }
2346 char *ptr;
2347 };
2348
2349 out_str_cleaner osc{out_str};
2350
2351 // NOTE: I am not sure if it is possible to get a string of size 0
2352 // from a printf() function. Let's throw for the time being in such a case,
2353 // as zero-length output complicates indexing below.
2354 if (ret == 0) {
2355 // LCOV_EXCL_START
2356 throw std::invalid_argument("The mpfr_asprintf() function returned an empty string");
2357 // LCOV_EXCL_STOP
2358 }
2359
2360 // We are going to do the filling
2361 // only if the stream width is larger
2362 // than the total size of the string repr.
2363 // NOTE: width > ret already implies width >= 0.
2364 if (width > ret) {
2365 // Create a std::string from the string outout
2366 // of printf().
2367 std::string tmp(out_str, out_str + ret);
2368
2369 // Determine the fill type.
2370 const auto fill = detail::stream_flags_to_fill(flags);
2371
2372 // Compute how much fill we need.
2373 const auto fill_size = detail::safe_cast<decltype(tmp.size())>(width - ret);
2374
2375 // Get the fill character.
2376 const auto fill_char = os.fill();
2377
2378 switch (fill) {
2379 case 1:
2380 // Left fill: fill characters at the end.
2381 tmp.insert(tmp.end(), fill_size, fill_char);
2382 break;
2383 case 2:
2384 // Right fill: fill characters at the beginning.
2385 tmp.insert(tmp.begin(), fill_size, fill_char);
2386 break;
2387 default: {
2388 assert(fill == 3);
2389
2390 // Internal fill: the fill characters are always after the sign (if present).
2391 const auto delta = static_cast<int>(tmp[0] == '+' || tmp[0] == '-');
2392 tmp.insert(tmp.begin() + delta, fill_size, fill_char);
2393 break;
2394 }
2395 }
2396
2397 os.write(tmp.data(), detail::safe_cast<std::streamsize>(tmp.size()));
2398 } else {
2399 os.write(out_str, detail::safe_cast<std::streamsize>(ret));
2400 }
2401
2402 // Reset the stream width to zero, like the operator<<() does for builtin types.
2403 // https://en.cppreference.com/w/cpp/io/manip/setw
2404 // Do it here so we ensure we don't alter the state of the stream until the very end.
2405 os.width(0);
2406
2407 return os;
2408 }
2409
2410 namespace detail
2411 {
2412
2413 // NOTE: don't put in unnamed namespace as
2414 // this needs do be just in detail:: for friendship
2415 // with real.
2416 template <typename F>
real_constant(const F & f,::mpfr_prec_t p)2417 inline real real_constant(const F &f, ::mpfr_prec_t p)
2418 {
2419 if (mppp_unlikely(!real_prec_check(p))) {
2420 throw std::invalid_argument("Cannot init a real constant with a precision of " + detail::to_string(p)
2421 + ": the value must be between " + detail::to_string(real_prec_min()) + " and "
2422 + detail::to_string(real_prec_max()));
2423 }
2424
2425 real retval{real::ptag{}, p, true};
2426 f(retval._get_mpfr_t(), MPFR_RNDN);
2427 return retval;
2428 }
2429
2430 } // namespace detail
2431
2432 // Pi constant.
real_pi(::mpfr_prec_t p)2433 real real_pi(::mpfr_prec_t p)
2434 {
2435 return detail::real_constant(::mpfr_const_pi, p);
2436 }
2437
real_pi(real & rop)2438 real &real_pi(real &rop)
2439 {
2440 ::mpfr_const_pi(rop._get_mpfr_t(), MPFR_RNDN);
2441 return rop;
2442 }
2443
real_log2(::mpfr_prec_t p)2444 real real_log2(::mpfr_prec_t p)
2445 {
2446 return detail::real_constant(::mpfr_const_log2, p);
2447 }
2448
real_log2(real & rop)2449 real &real_log2(real &rop)
2450 {
2451 ::mpfr_const_log2(rop._get_mpfr_t(), MPFR_RNDN);
2452 return rop;
2453 }
2454
real_euler(::mpfr_prec_t p)2455 real real_euler(::mpfr_prec_t p)
2456 {
2457 return detail::real_constant(::mpfr_const_euler, p);
2458 }
2459
real_euler(real & rop)2460 real &real_euler(real &rop)
2461 {
2462 ::mpfr_const_euler(rop._get_mpfr_t(), MPFR_RNDN);
2463 return rop;
2464 }
2465
real_catalan(::mpfr_prec_t p)2466 real real_catalan(::mpfr_prec_t p)
2467 {
2468 return detail::real_constant(::mpfr_const_catalan, p);
2469 }
2470
real_catalan(real & rop)2471 real &real_catalan(real &rop)
2472 {
2473 ::mpfr_const_catalan(rop._get_mpfr_t(), MPFR_RNDN);
2474 return rop;
2475 }
2476
2477 namespace detail
2478 {
2479
2480 namespace
2481 {
2482
2483 // std::size_t addition with overflow checking.
rbs_checked_add(std::size_t a,std::size_t b)2484 std::size_t rbs_checked_add(std::size_t a, std::size_t b)
2485 {
2486 // LCOV_EXCL_START
2487 if (mppp_unlikely(a > std::numeric_limits<std::size_t>::max() - b)) {
2488 throw std::overflow_error("Overflow detected in the computation of the binary size of a real");
2489 }
2490 // LCOV_EXCL_STOP
2491
2492 return a + b;
2493 }
2494
2495 // Turn an MPFR precision into the number of limbs
2496 // necessary to represent the significand of
2497 // a number with that precision.
rbs_prec_to_nlimbs(::mpfr_prec_t p)2498 std::size_t rbs_prec_to_nlimbs(::mpfr_prec_t p)
2499 {
2500 // NOTE: currently both mpfr_prec_t and GMP_NUMB_BITS
2501 // are signed.
2502 using uprec_t = std::make_unsigned<::mpfr_prec_t>::type;
2503 return safe_cast<std::size_t>(static_cast<uprec_t>(p / GMP_NUMB_BITS + static_cast<int>((p % GMP_NUMB_BITS) != 0)));
2504 }
2505
2506 // Turn an MPFR precision into the number of bytes
2507 // necessary to represent the significand of
2508 // a number with that precision.
rbs_prec_to_size(::mpfr_prec_t p)2509 std::size_t rbs_prec_to_size(::mpfr_prec_t p)
2510 {
2511 const auto nlimbs = rbs_prec_to_nlimbs(p);
2512
2513 // LCOV_EXCL_START
2514 if (mppp_unlikely(nlimbs > std::numeric_limits<std::uint32_t>::max() / sizeof(::mp_limb_t))) {
2515 throw std::overflow_error("Overflow detected in the computation of the binary size of a real");
2516 }
2517 // LCOV_EXCL_STOP
2518
2519 return nlimbs * sizeof(::mp_limb_t);
2520 }
2521
2522 // The base binary size of a real: accounts for the size of precision,
2523 // sign bit and exponent.
rbs_base_size()2524 std::size_t rbs_base_size()
2525 {
2526 // Precision.
2527 auto retval = sizeof(::mpfr_prec_t);
2528 // Sign bit.
2529 retval = rbs_checked_add(retval, sizeof(::mpfr_sign_t));
2530 // Exponent.
2531 return rbs_checked_add(retval, sizeof(::mpfr_exp_t));
2532 }
2533
2534 // The minimum binary size of a real (a real must have at least 1 limb
2535 // of data).
rbs_min_size()2536 std::size_t rbs_min_size()
2537 {
2538 return rbs_checked_add(rbs_base_size(), sizeof(::mp_limb_t));
2539 }
2540
2541 // Load the serializaed precision value from a char buffer.
rbs_read_prec(const char * src)2542 ::mpfr_prec_t rbs_read_prec(const char *src)
2543 {
2544 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2545 ::mpfr_prec_t p;
2546 std::copy(src, src + sizeof(p), reinterpret_cast<char *>(&p));
2547 return p;
2548 }
2549
2550 } // namespace
2551
2552 } // namespace detail
2553
2554 #if defined(MPPP_MPFR_HAVE_MPFR_GET_STR_NDIGITS)
2555
2556 // Get the number of significant digits required for a round-tripping representation.
get_str_ndigits(int base) const2557 std::size_t real::get_str_ndigits(int base) const
2558 {
2559 if (mppp_unlikely(base < 2 || base > 62)) {
2560 throw std::invalid_argument(
2561 "Invalid base value for get_str_ndigits(): the base must be in the [2,62] range, but it is "
2562 + std::to_string(base) + " instead");
2563 }
2564
2565 return ::mpfr_get_str_ndigits(base, get_prec());
2566 }
2567
get_str_ndigits(const real & r,int base)2568 std::size_t get_str_ndigits(const real &r, int base)
2569 {
2570 return r.get_str_ndigits(base);
2571 }
2572
2573 #endif
2574
2575 // Size of the serialised binary representation: base size + limbs data.
binary_size() const2576 std::size_t real::binary_size() const
2577 {
2578 return detail::rbs_checked_add(detail::rbs_base_size(), detail::rbs_prec_to_size(get_prec()));
2579 }
2580
binary_size(const real & x)2581 std::size_t binary_size(const real &x)
2582 {
2583 return x.binary_size();
2584 }
2585
2586 // Save to a char buffer, given a binary size computed
2587 // via binary_size().
binary_save_impl(char * dest,std::size_t bs) const2588 void real::binary_save_impl(char *dest, std::size_t bs) const
2589 {
2590 // Save the precision.
2591 std::copy(reinterpret_cast<const char *>(&m_mpfr._mpfr_prec),
2592 reinterpret_cast<const char *>(&m_mpfr._mpfr_prec) + sizeof(::mpfr_prec_t), dest);
2593 dest += sizeof(::mpfr_prec_t);
2594
2595 // Save the sign bit.
2596 std::copy(reinterpret_cast<const char *>(&m_mpfr._mpfr_sign),
2597 reinterpret_cast<const char *>(&m_mpfr._mpfr_sign) + sizeof(::mpfr_sign_t), dest);
2598 dest += sizeof(::mpfr_sign_t);
2599
2600 // Save the exponent.
2601 std::copy(reinterpret_cast<const char *>(&m_mpfr._mpfr_exp),
2602 reinterpret_cast<const char *>(&m_mpfr._mpfr_exp) + sizeof(::mpfr_exp_t), dest);
2603 dest += sizeof(::mpfr_exp_t);
2604
2605 // Save the significand.
2606 std::copy(reinterpret_cast<const char *>(m_mpfr._mpfr_d),
2607 reinterpret_cast<const char *>(m_mpfr._mpfr_d) + (bs - detail::rbs_base_size()), dest);
2608 }
2609
2610 // Save to a char buffer.
binary_save(char * dest) const2611 std::size_t real::binary_save(char *dest) const
2612 {
2613 const auto bs = binary_size();
2614 binary_save_impl(dest, bs);
2615 return bs;
2616 }
2617
2618 // Save to a std::vector buffer.
binary_save(std::vector<char> & dest) const2619 std::size_t real::binary_save(std::vector<char> &dest) const
2620 {
2621 const auto bs = binary_size();
2622 if (dest.size() < bs) {
2623 dest.resize(detail::safe_cast<decltype(dest.size())>(bs));
2624 }
2625 binary_save_impl(dest.data(), bs);
2626 return bs;
2627 }
2628
2629 // Save to a stream.
binary_save(std::ostream & dest) const2630 std::size_t real::binary_save(std::ostream &dest) const
2631 {
2632 const auto bs = binary_size();
2633 // NOTE: there does not seem to be a reliable way of detecting how many bytes
2634 // are actually written via write(). See the question here and especially the comments:
2635 // https://stackoverflow.com/questions/14238572/how-many-bytes-actually-written-by-ostreamwrite
2636 // Seems almost like tellp() would work, but if an error occurs in the stream, then
2637 // it returns unconditionally -1, so it is not very useful for our purposes.
2638 // Thus, we will just return 0 on failure, and the full binary size otherwise.
2639 //
2640 // Write the raw data to stream.
2641 dest.write(reinterpret_cast<const char *>(&m_mpfr._mpfr_prec),
2642 detail::safe_cast<std::streamsize>(sizeof(::mpfr_prec_t)));
2643 if (!dest.good()) {
2644 // !dest.good() means that the last write operation failed. Bail out now.
2645 return 0;
2646 }
2647
2648 dest.write(reinterpret_cast<const char *>(&m_mpfr._mpfr_sign),
2649 detail::safe_cast<std::streamsize>(sizeof(::mpfr_sign_t)));
2650 if (!dest.good()) {
2651 // LCOV_EXCL_START
2652 return 0;
2653 // LCOV_EXCL_STOP
2654 }
2655
2656 dest.write(reinterpret_cast<const char *>(&m_mpfr._mpfr_exp),
2657 detail::safe_cast<std::streamsize>(sizeof(::mpfr_exp_t)));
2658 if (!dest.good()) {
2659 // LCOV_EXCL_START
2660 return 0;
2661 // LCOV_EXCL_STOP
2662 }
2663
2664 dest.write(reinterpret_cast<const char *>(m_mpfr._mpfr_d),
2665 detail::safe_cast<std::streamsize>(bs - detail::rbs_base_size()));
2666 return dest.good() ? bs : 0u;
2667 }
2668
2669 // Load from a char buffer of unknown size.
binary_load_impl(const char * src)2670 std::size_t real::binary_load_impl(const char *src)
2671 {
2672 // Fetch the precision first.
2673 const auto p = detail::rbs_read_prec(src);
2674 src += sizeof(p);
2675
2676 // Fetch the sign bit.
2677 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2678 ::mpfr_sign_t sb;
2679 std::copy(src, src + sizeof(sb), reinterpret_cast<char *>(&sb));
2680 src += sizeof(sb);
2681
2682 // Fetch the exponent.
2683 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2684 ::mpfr_exp_t e;
2685 std::copy(src, src + sizeof(e), reinterpret_cast<char *>(&e));
2686 src += sizeof(e);
2687
2688 // Compute the size in bytes of the significand from the precision.
2689 const auto sbs = detail::rbs_prec_to_size(p);
2690
2691 // Compute the return value.
2692 auto retval = detail::rbs_checked_add(detail::rbs_base_size(), sbs);
2693
2694 // Set the precision of this.
2695 set_prec(p);
2696 // NOTE: from now on, everything is noexcept.
2697 // Set the sign bit.
2698 m_mpfr._mpfr_sign = sb;
2699 // Set the exponent.
2700 m_mpfr._mpfr_exp = e;
2701 // Copy over the limbs.
2702 std::copy(src, src + sbs, reinterpret_cast<char *>(m_mpfr._mpfr_d));
2703
2704 return retval;
2705 }
2706
2707 // Load from a char buffer of size 'size' belonging to a container of type 'name'.
binary_load_impl(const char * src,std::size_t size,const char * name)2708 std::size_t real::binary_load_impl(const char *src, std::size_t size, const char *name)
2709 {
2710 if (mppp_unlikely(size < detail::rbs_min_size())) {
2711 throw std::invalid_argument(std::string("Invalid size detected in the deserialisation of a real via a ") + name
2712 + ": the " + name + " size must be at least "
2713 + std::to_string(detail::rbs_min_size()) + " bytes, but it is only "
2714 + std::to_string(size) + " bytes");
2715 }
2716
2717 // Read the precision.
2718 const auto p = detail::rbs_read_prec(src);
2719
2720 // Determine the binary size from the loaded precision.
2721 const auto expected_size = detail::rbs_checked_add(detail::rbs_base_size(), detail::rbs_prec_to_size(p));
2722
2723 // Check that the buffer contains at least the expected amount of data.
2724 if (mppp_unlikely(size < expected_size)) {
2725 throw std::invalid_argument(std::string("Invalid size detected in the deserialisation of a real via a ") + name
2726 + ": the " + name + " size must be at least " + std::to_string(expected_size)
2727 + " bytes, but it is only " + std::to_string(size) + " bytes");
2728 }
2729
2730 return binary_load_impl(src);
2731 }
2732
2733 // Load from a char buffer of unknown size.
binary_load(const char * src)2734 std::size_t real::binary_load(const char *src)
2735 {
2736 return binary_load_impl(src);
2737 }
2738
2739 // Load from a std::vector.
binary_load(const std::vector<char> & v)2740 std::size_t real::binary_load(const std::vector<char> &v)
2741 {
2742 return binary_load_impl(v.data(), detail::safe_cast<std::size_t>(v.size()), "std::vector");
2743 }
2744
2745 // Load from an input stream.
binary_load(std::istream & src)2746 std::size_t real::binary_load(std::istream &src)
2747 {
2748 // Let's try to read the precision first.
2749 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2750 ::mpfr_prec_t p;
2751 src.read(reinterpret_cast<char *>(&p), detail::safe_cast<std::streamsize>(sizeof(p)));
2752 if (!src.good()) {
2753 // Something went wrong with reading, return 0.
2754 return 0;
2755 }
2756
2757 // The sign bit.
2758 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2759 ::mpfr_sign_t sb;
2760 src.read(reinterpret_cast<char *>(&sb), detail::safe_cast<std::streamsize>(sizeof(sb)));
2761 if (!src.good()) {
2762 return 0;
2763 }
2764
2765 // The exponent.
2766 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2767 ::mpfr_exp_t e;
2768 src.read(reinterpret_cast<char *>(&e), detail::safe_cast<std::streamsize>(sizeof(e)));
2769 if (!src.good()) {
2770 return 0;
2771 }
2772
2773 // Compute the size in bytes of the significand from the precision.
2774 const auto sbs = detail::rbs_prec_to_size(p);
2775
2776 // Read the limb data into a local buffer.
2777 MPPP_MAYBE_TLS std::vector<char> buffer;
2778 buffer.resize(detail::safe_cast<decltype(buffer.size())>(sbs));
2779 src.read(buffer.data(), detail::safe_cast<std::streamsize>(sbs));
2780 if (!src.good()) {
2781 // Something went wrong with reading, return 0.
2782 return 0;
2783 }
2784
2785 // Compute the return value.
2786 auto retval = detail::rbs_checked_add(detail::rbs_base_size(), sbs);
2787
2788 // Set the precision of this.
2789 set_prec(p);
2790 // NOTE: from now on, everything is noexcept.
2791 // Set the sign bit.
2792 m_mpfr._mpfr_sign = sb;
2793 // Set the exponent.
2794 m_mpfr._mpfr_exp = e;
2795 // Copy over the limbs.
2796 std::copy(buffer.begin(), buffer.end(), reinterpret_cast<char *>(m_mpfr._mpfr_d));
2797
2798 return retval;
2799 }
2800
2801 #if defined(MPPP_WITH_BOOST_S11N)
2802
2803 // Fast serialization implementations for Boost's binary archives.
save(boost::archive::binary_oarchive & ar,unsigned) const2804 void real::save(boost::archive::binary_oarchive &ar, unsigned) const
2805 {
2806 MPPP_MAYBE_TLS std::vector<char> buffer;
2807 binary_save(buffer);
2808
2809 // Record the size and the raw data.
2810 ar << buffer.size();
2811 ar << boost::serialization::make_binary_object(buffer.data(), detail::safe_cast<std::size_t>(buffer.size()));
2812 }
2813
load(boost::archive::binary_iarchive & ar,unsigned)2814 void real::load(boost::archive::binary_iarchive &ar, unsigned)
2815 {
2816 MPPP_MAYBE_TLS std::vector<char> buffer;
2817
2818 // Recover the size.
2819 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2820 decltype(buffer.size()) s;
2821 ar >> s;
2822 buffer.resize(s);
2823
2824 ar >> boost::serialization::make_binary_object(buffer.data(), detail::safe_cast<std::size_t>(buffer.size()));
2825
2826 binary_load(buffer);
2827 }
2828
2829 #endif
2830
2831 } // namespace mppp
2832
2833 #if defined(_MSC_VER)
2834
2835 #undef _SCL_SECURE_NO_WARNINGS
2836
2837 #endif
2838