1 // Copyright 2016-2021 Francesco Biscani (bluescarni@gmail.com)
2 //
3 // This file is part of the mp++ library.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla
6 // Public License v. 2.0. If a copy of the MPL was not distributed
7 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #ifndef MPPP_REAL128_HPP
10 #define MPPP_REAL128_HPP
11 
12 #include <mp++/config.hpp>
13 
14 #if defined(MPPP_WITH_QUADMATH)
15 
16 #include <cassert>
17 #include <cmath>
18 #include <complex>
19 #include <cstdint>
20 #include <limits>
21 #include <ostream>
22 #include <stdexcept>
23 #include <string>
24 #include <tuple>
25 #include <type_traits>
26 #include <vector>
27 
28 #if defined(MPPP_HAVE_STRING_VIEW)
29 
30 #include <string_view>
31 
32 #endif
33 
34 #if defined(MPPP_WITH_BOOST_S11N)
35 
36 #include <boost/archive/binary_iarchive.hpp>
37 #include <boost/archive/binary_oarchive.hpp>
38 #include <boost/serialization/access.hpp>
39 #include <boost/serialization/split_member.hpp>
40 #include <boost/serialization/string.hpp>
41 #include <boost/serialization/tracking.hpp>
42 
43 #endif
44 
45 #include <mp++/concepts.hpp>
46 #include <mp++/detail/gmp.hpp>
47 #include <mp++/detail/type_traits.hpp>
48 #include <mp++/detail/utils.hpp>
49 #include <mp++/detail/visibility.hpp>
50 #include <mp++/fwd.hpp>
51 #include <mp++/integer.hpp>
52 #include <mp++/rational.hpp>
53 
54 namespace mppp
55 {
56 
57 namespace detail
58 {
59 
60 // Machinery for low-level manipulation of __float128. Inspired by:
61 // https://github.com/gcc-mirror/gcc/blob/master/libquadmath/quadmath-imp.h
62 // Note that the union machinery is technically UB, but well-defined on GCC as
63 // an extension:
64 // https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type%2Dpunning
65 // quadmath so far is limited to GCC and perhaps clang, which I'd imagine
66 // implements the same extension for GCC compatibility. So we should be ok.
67 
68 // The ieee fields.
69 struct ieee_t {
70 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
71     std::uint_least8_t negative : 1;
72     std::uint_least16_t exponent : 15;
73     std::uint_least64_t mant_high : 48;
74     std::uint_least64_t mant_low : 64;
75 #else
76     std::uint_least64_t mant_low : 64;
77     std::uint_least64_t mant_high : 48;
78     std::uint_least16_t exponent : 15;
79     std::uint_least8_t negative : 1;
80 #endif
81 }
82 #if defined(__MINGW32__)
83 // On mingw targets the ms-bitfields option is active by default.
84 // Therefore enforce gnu-bitfield style.
85 __attribute__((gcc_struct))
86 #endif
87 ;
88 
89 // The union.
90 union ieee_float128 {
91     __float128 value;
92     ieee_t i_eee;
93 };
94 
95 // Wrappers for use in various functions below (so that
96 // we don't have to include quadmath.h here).
97 MPPP_DLL_PUBLIC __float128 scalbnq(__float128, int);
98 MPPP_DLL_PUBLIC __float128 scalblnq(__float128, long);
99 
100 // For internal use only.
101 template <typename T>
102 using is_real128_mppp_interoperable = disjunction<is_integer<T>, is_rational<T>>;
103 
104 // Story time!
105 //
106 // Since 3.9, clang supports the __float128 type. However, interactions between long double and __float128 are disabled.
107 // The rationale is given here:
108 //
109 // https://reviews.llvm.org/D15120
110 //
111 // Basically, it boils down to the fact that on at least one platform (PPC) the long double type (which is implemented
112 // as a double-double) can represent exactly some values that __float128 cannot (double-double is a strange beast).
113 // On the other hand, __float128 can also
114 // represent values that double-double cannot, so it is not clear which floating-point type should have the higher
115 // rank. Note that C and C++ mandate, for the standard FP types, that higher rank FPs can represent all values
116 // that lower rank FPs can. So, the decision was made to just disable the interaction. This is in line with the
117 // following TS for che C language ("Floating-point extensions for C"):
118 //
119 // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1945.pdf (page 20)
120 //
121 // (this is not necessarily a very good way of doing things though, as it seems to create portability concerns)
122 //
123 // This is part of a larger problem having to do with the fact that, while the properties of __float128 are exactly
124 // specified (i.e., quad-precision IEEE), the properties of the standard floating-point types are *not*. So, a
125 // *truly* generic real128 class would need, for instance:
126 //
127 // - to check which C++ types can interact with __float128 and enable only those,
128 // - possibly return types other than real128 in binary operations (maybe long double is strictly bigger than quad
129 //   precision?),
130 // - etc. etc.
131 //
132 // For the moment, though, it seems like on all platforms __float128 is at least the same rank as double. For long
133 // double, clang and GCC diverge, and we follow whatever the compiler is doing. So we just hard-code the behaviour
134 // here, we can always write a more sophisticated solution later if the need arises.
135 //
136 // NOTE: since version 7 the behaviour of clang changed, now matching GCC. See the logic implemented
137 // in config.hpp.
138 
139 // For internal use only.
140 template <typename T>
141 using is_real128_cpp_interoperable = detail::conjunction<is_cpp_arithmetic<T>
142 #if !defined(MPPP_FLOAT128_WITH_LONG_DOUBLE)
143                                                          ,
144                                                          detail::negation<std::is_same<T, long double>>
145 #endif
146                                                          >;
147 
148 } // namespace detail
149 
150 template <typename T>
151 using is_real128_interoperable
152     = detail::disjunction<detail::is_real128_cpp_interoperable<T>, detail::is_real128_mppp_interoperable<T>>;
153 
154 #if defined(MPPP_HAVE_CONCEPTS)
155 
156 template <typename T>
157 MPPP_CONCEPT_DECL real128_interoperable = is_real128_interoperable<T>::value;
158 
159 #endif
160 
161 template <typename T>
162 using is_real128_cpp_complex = detail::conjunction<is_cpp_complex<T>
163 #if !defined(MPPP_FLOAT128_WITH_LONG_DOUBLE)
164                                                    ,
165                                                    detail::negation<std::is_same<T, std::complex<long double>>>
166 #endif
167                                                    >;
168 
169 #if defined(MPPP_HAVE_CONCEPTS)
170 
171 template <typename T>
172 MPPP_CONCEPT_DECL real128_cpp_complex = is_real128_cpp_complex<T>::value;
173 
174 #endif
175 
176 template <typename T, typename U>
177 using are_real128_op_types
178     = detail::disjunction<detail::conjunction<std::is_same<T, real128>, std::is_same<U, real128>>,
179                           detail::conjunction<std::is_same<T, real128>, is_real128_interoperable<U>>,
180                           detail::conjunction<std::is_same<U, real128>, is_real128_interoperable<T>>>;
181 
182 #if defined(MPPP_HAVE_CONCEPTS)
183 
184 template <typename T, typename U>
185 MPPP_CONCEPT_DECL real128_op_types = are_real128_op_types<T, U>::value;
186 
187 #endif
188 
189 // Fwd declare the abs() function.
190 #if !defined(__INTEL_COMPILER)
191 constexpr
192 #endif
193     real128
194     abs(const real128 &);
195 
196 // For the future:
197 // - the constructor from integer *may* be implemented in a faster way by reading directly the hi/lo parts
198 //   and writing them to the ieee union (instead right now we are using __float128 arithmetics and quadmath
199 //   functions). Make sure to benchmark first though...
200 // - should we change the cast operator to C++ types to check the result of the conversion?
201 
202 // Quadruple-precision floating-point class.
203 class MPPP_DLL_PUBLIC real128
204 {
205 #if defined(MPPP_WITH_BOOST_S11N)
206     // Boost serialization support.
207     friend class boost::serialization::access;
208 
209     template <typename Archive>
save(Archive & ar,unsigned) const210     void save(Archive &ar, unsigned) const
211     {
212         ar << to_string();
213     }
214 
215     template <typename Archive>
load(Archive & ar,unsigned)216     void load(Archive &ar, unsigned)
217     {
218         std::string tmp;
219         ar >> tmp;
220 
221         *this = real128{tmp};
222     }
223 
224     // Overloads for binary archives.
225     void save(boost::archive::binary_oarchive &, unsigned) const;
226     void load(boost::archive::binary_iarchive &, unsigned);
227 
228     BOOST_SERIALIZATION_SPLIT_MEMBER()
229 #endif
230 
231     // Number of digits in the significand.
232     static constexpr unsigned sig_digits = 113;
233 
234 public:
235     // Default constructor.
real128()236     constexpr real128() : m_value(0) {}
237 
238     // Trivial copy constructor.
239     real128(const real128 &) = default;
240     // Trivial move constructor.
241     real128(real128 &&) = default;
242 
243     // Constructor from a quadruple-precision floating-point value.
244     // NOTE: early GCC versions seem to exhibit constexpr
245     // failures if x is passed by const reference.
real128(__float128 x)246     constexpr explicit real128(
247 #if defined(__GNUC__) && __GNUC__ < 5
248         __float128 x
249 #else
250         const __float128 &x
251 #endif
252         )
253         : m_value(x)
254     {
255     }
256 
257 private:
258     // Cast an integer to a __float128.
259     template <std::size_t SSize>
cast_to_float128(const integer<SSize> & n)260     static __float128 cast_to_float128(const integer<SSize> &n)
261     {
262         // Special case for zero.
263         const auto n_sgn = n.sgn();
264         if (!n_sgn) {
265             return 0;
266         }
267 
268         // Get the pointer to the limbs, and the size in limbs and bits.
269         const ::mp_limb_t *ptr = n.is_static() ? n._get_union().g_st().m_limbs.data() : n._get_union().g_dy()._mp_d;
270         const std::size_t n_bits = n.nbits();
271 
272         // Let's get the size in limbs from the size in bits, as we already made the effort.
273         const auto rem_bits = n_bits % unsigned(GMP_NUMB_BITS);
274         std::size_t ls = n_bits / unsigned(GMP_NUMB_BITS) + static_cast<bool>(rem_bits);
275         assert(ls && n_bits && ls == n.size());
276 
277         // Init value with the most significant limb, and move to the next limb.
278         __float128 retval = ptr[--ls] & GMP_NUMB_MASK;
279 
280         // Number of bits read so far from n: it is the size in bits of the top limb.
281         auto read_bits = static_cast<unsigned>(rem_bits ? rem_bits : unsigned(GMP_NUMB_BITS));
282         assert(read_bits);
283 
284         // Keep on reading as long as we have limbs and as long as we haven't read enough bits
285         // to fill up the significand of retval.
286         while (ls && read_bits < sig_digits) {
287             // Number of bits to be read from the current limb: GMP_NUMB_BITS or less.
288             const unsigned rbits = detail::c_min(unsigned(GMP_NUMB_BITS), sig_digits - read_bits);
289 
290             // Shift retval by rbits.
291             // NOTE: safe to cast to int here, as rbits is not greater than GMP_NUMB_BITS which in turn fits in int.
292             retval = detail::scalbnq(retval, static_cast<int>(rbits));
293 
294             // Add the bottom part, and move to the next limb. We might need to remove lower bits
295             // in case rbits is not exactly GMP_NUMB_BITS.
296             retval += (ptr[--ls] & GMP_NUMB_MASK) >> (unsigned(GMP_NUMB_BITS) - rbits);
297 
298             // Update the number of read bits.
299             // NOTE: read_bits can never be increased past sig_digits, due to the definition of rbits.
300             // Hence, this addition can never overflow (as sig_digits is unsigned itself).
301             read_bits += rbits;
302         }
303 
304         if (read_bits < n_bits) {
305             // We did not read from n all its bits. This means that n has more bits than the quad-precision
306             // significand, and thus we need to multiply this by 2**unread_bits.
307             // Use the long variant of scalbn() to maximise the range.
308             retval = detail::scalblnq(retval, detail::safe_cast<long>(n_bits - read_bits));
309         }
310 
311         // Fix the sign as needed.
312         if (n_sgn == -1) {
313             retval = -retval;
314         }
315 
316         return retval;
317     }
318 
319     // Cast a rational to a __float128.
320     template <std::size_t SSize>
cast_to_float128(const rational<SSize> & q)321     static __float128 cast_to_float128(const rational<SSize> &q)
322     {
323         const auto n_bits = q.get_num().nbits();
324         const auto d_bits = q.get_den().nbits();
325 
326         if (n_bits <= sig_digits && d_bits <= sig_digits) {
327             // Both num/den don't have more bits than quad's significand. We can just convert
328             // them and divide.
329             return real128{q.get_num()}.m_value / real128{q.get_den()}.m_value;
330         } else if (n_bits > sig_digits && d_bits <= sig_digits) {
331             // Num's bit size is larger than quad's significand, den's is not. We will shift num down,
332             // do the conversion, and then recover the shifted bits in the float128.
333             MPPP_MAYBE_TLS integer<SSize> n;
334 
335             const auto shift = n_bits - sig_digits;
336             tdiv_q_2exp(n, q.get_num(), detail::safe_cast<::mp_bitcnt_t>(shift));
337             auto retval = real128{n}.m_value / real128{q.get_den()}.m_value;
338             return detail::scalblnq(retval, detail::safe_cast<long>(shift));
339         } else if (n_bits <= sig_digits && d_bits > sig_digits) {
340             // The opposite of above.
341             MPPP_MAYBE_TLS integer<SSize> d;
342 
343             const auto shift = d_bits - sig_digits;
344             tdiv_q_2exp(d, q.get_den(), detail::safe_cast<::mp_bitcnt_t>(shift));
345             auto retval = real128{q.get_num()}.m_value / real128{d}.m_value;
346             return detail::scalblnq(retval, detail::negate_unsigned<long>(shift));
347         } else {
348             // Both num and den have more bits than quad's significand. We will downshift
349             // both until they have 113 bits, do the division, and then recover the shifted bits.
350             MPPP_MAYBE_TLS integer<SSize> n;
351             MPPP_MAYBE_TLS integer<SSize> d;
352 
353             const auto n_shift = n_bits - sig_digits;
354             const auto d_shift = d_bits - sig_digits;
355 
356             tdiv_q_2exp(n, q.get_num(), detail::safe_cast<::mp_bitcnt_t>(n_shift));
357             tdiv_q_2exp(d, q.get_den(), detail::safe_cast<::mp_bitcnt_t>(d_shift));
358 
359             auto retval = real128{n}.m_value / real128{d}.m_value;
360             if (n_shift >= d_shift) {
361                 return detail::scalblnq(retval, detail::safe_cast<long>(n_shift - d_shift));
362             } else {
363                 return detail::scalblnq(retval, detail::negate_unsigned<long>(d_shift - n_shift));
364             }
365         }
366     }
367 
368     // Cast C++ types to a __float128.
369     template <typename T>
cast_to_float128(const T & x)370     static constexpr __float128 cast_to_float128(const T &x)
371     {
372         return static_cast<__float128>(x);
373     }
374 
375 public:
376     // Constructor from interoperable types.
377 #if defined(MPPP_HAVE_CONCEPTS)
378     template <real128_interoperable T>
379 #else
380     template <typename T, detail::enable_if_t<is_real128_interoperable<T>::value, int> = 0>
381 #endif
real128(const T & x)382     constexpr real128(const T &x) : m_value(cast_to_float128(x))
383     {
384     }
385     // Constructor from std::complex.
386 #if defined(MPPP_HAVE_CONCEPTS)
387     template <real128_cpp_complex T>
388 #else
389     template <typename T, detail::enable_if_t<is_real128_cpp_complex<T>::value, int> = 0>
390 #endif
real128(const T & c)391     MPPP_CONSTEXPR_14 explicit real128(const T &c)
392         : m_value(c.imag() == 0
393                       ? c.real()
394                       : throw std::domain_error(
395                           "Cannot construct a real128 from a complex C++ value with a non-zero imaginary part of "
396                           + detail::to_string(c.imag())))
397     {
398     }
399 
400 private:
401     // A tag to call private ctors.
402     struct ptag {
403     };
404     explicit real128(const ptag &, const char *);
405     explicit real128(const ptag &, const std::string &);
406 #if defined(MPPP_HAVE_STRING_VIEW)
407     explicit real128(const ptag &, const std::string_view &);
408 #endif
409 
410 public:
411     // Constructor from string.
412 #if defined(MPPP_HAVE_CONCEPTS)
413     template <string_type T>
414 #else
415     template <typename T, detail::enable_if_t<is_string_type<T>::value, int> = 0>
416 #endif
417     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real128(const T & s)418     explicit real128(const T &s) : real128(ptag{}, s)
419     {
420     }
421     // Constructor from range of characters.
422     explicit real128(const char *, const char *);
423 
424     ~real128() = default;
425 
426     // Trivial copy assignment operator.
427     real128 &operator=(const real128 &) = default;
428     // Trivial move assignment operator.
429     real128 &operator=(real128 &&) = default;
430 
431     // Assignment from a quadruple-precision floating-point value.
operator =(const __float128 & x)432     MPPP_CONSTEXPR_14 real128 &operator=(const __float128 &x)
433     {
434         m_value = x;
435         return *this;
436     }
437 
438     // Assignment from interoperable types.
439 #if defined(MPPP_HAVE_CONCEPTS)
440     template <real128_interoperable T>
441 #else
442     template <typename T, detail::enable_if_t<is_real128_interoperable<T>::value, int> = 0>
443 #endif
operator =(const T & x)444     MPPP_CONSTEXPR_14 real128 &operator=(const T &x)
445     {
446         // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
447         return *this = real128{x};
448     }
449 
450     // Assignment from C++ complex.
451 #if defined(MPPP_HAVE_CONCEPTS)
452     template <real128_cpp_complex T>
453 #else
454     template <typename T, detail::enable_if_t<is_real128_cpp_complex<T>::value, int> = 0>
455 #endif
operator =(const T & c)456     MPPP_CONSTEXPR_14 real128 &operator=(const T &c)
457     {
458         // NOTE: icpc does not like __builtin_expect()
459         // in constexpr contexts.
460 #if defined(__INTEL_COMPILER)
461         if (c.imag() != 0) {
462 #else
463         if (mppp_unlikely(c.imag()) != 0) {
464 #endif
465             throw std::domain_error("Cannot assign a complex C++ value with a non-zero imaginary part of "
466                                     + detail::to_string(c.imag()) + " to a real128");
467         }
468         // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
469         return *this = c.real();
470     }
471     // Declaration of the assignments from
472     // other mp++ classes.
473 #if defined(MPPP_WITH_MPFR)
474     real128 &operator=(const real &);
475 #endif
476     MPPP_CONSTEXPR_14 real128 &operator=(const complex128 &);
477 #if defined(MPPP_WITH_MPC)
478     real128 &operator=(const complex &);
479 #endif
480 
481     // Assignment from string.
482 #if defined(MPPP_HAVE_CONCEPTS)
483     template <string_type T>
484 #else
485     template <typename T, detail::enable_if_t<is_string_type<T>::value, int> = 0>
486 #endif
487     real128 &operator=(const T &s)
488     {
489         // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
490         return *this = real128{s};
491     }
492 
493     // Conversion to quadruple-precision floating-point.
494     constexpr explicit operator __float128() const
495     {
496         return m_value;
497     }
498 
499 private:
500     // Conversion to C++ types.
501     template <typename T>
502     MPPP_NODISCARD constexpr T dispatch_conversion(std::true_type) const
503     {
504         return static_cast<T>(m_value);
505     }
506 
507     // Conversion to integer.
508     template <std::size_t SSize>
509     bool mppp_conversion(integer<SSize> &rop) const
510     {
511         // Build the union and assign the value.
512         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
513         detail::ieee_float128 ief;
514         ief.value = m_value;
515         if (mppp_unlikely(ief.i_eee.exponent == 32767u)) {
516             // Inf or nan, not representable by integer.
517             return false;
518         }
519         // Determine the real exponent. 16383 is the offset of the representation,
520         // 112 is because we need to left shift the significand by 112 positions
521         // in order to turn it into an integral value.
522         const auto exponent = static_cast<long>(ief.i_eee.exponent) - (16383l + 112);
523         if (!ief.i_eee.exponent || exponent < -112) {
524             // Zero stored exponent means subnormal number, and if the real exponent is too small
525             // we end up with a value with abs less than 1. In such cases, just return 0.
526             rop.set_zero();
527             return true;
528         }
529         // Value is normalised and not less than 1 in abs. We can proceed.
530         rop.set_one();
531         if (exponent >= 0) {
532             // Non-negative exponent means that we will have to take the significand
533             // converted to an integer and further left shift it.
534             rop <<= 112u;
535             rop += integer<SSize>{ief.i_eee.mant_high} << 64u;
536             rop += ief.i_eee.mant_low;
537             rop <<= static_cast<unsigned long>(exponent);
538         } else {
539             // NOTE: the idea here is to avoid shifting up and then shifting back down,
540             // as that might trigger a promotion to dynamic storage in retval. Instead,
541             // we offset the shifts by the (negative) exponent, which is here guaranteed
542             // to be in the [-112,-1] range.
543             rop <<= static_cast<unsigned>(112 + exponent);
544             if (exponent > -64) {
545                 // We need to right shift less than 64 bits. This means that some bits from the low
546                 // word of the significand survive.
547                 // NOTE: need to do the left shift in multiprecision here, as the final result
548                 // might spill over the 64 bit range.
549                 rop += integer<SSize>{ief.i_eee.mant_high} << static_cast<unsigned>(exponent + 64);
550                 rop += ief.i_eee.mant_low >> static_cast<unsigned>(-exponent);
551             } else {
552                 // We need to right shift more than 64 bits, so none of the bits in the low word survive.
553                 // NOTE: here the right shift will be in the [0,48] range, so we can do it directly
554                 // on a C++ builtin type (i_eee.mant_high gives a 64bit int).
555                 rop += ief.i_eee.mant_high >> static_cast<unsigned>(-(exponent + 64));
556             }
557         }
558         // Adjust the sign.
559         if (ief.i_eee.negative) {
560             rop.neg();
561         }
562         return true;
563     }
564 
565     // Conversion to rational.
566     template <std::size_t SSize>
567     bool mppp_conversion(rational<SSize> &rop) const
568     {
569         // Build the union and assign the value.
570         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
571         detail::ieee_float128 ief;
572         ief.value = m_value;
573         if (mppp_unlikely(ief.i_eee.exponent == 32767u)) {
574             // Inf or nan, not representable by rational.
575             return false;
576         }
577         rop._get_num().set_zero();
578         rop._get_den().set_one();
579         if (ief.i_eee.exponent) {
580             // Normal number.
581             // Determine the real exponent.
582             const auto exponent = static_cast<long>(ief.i_eee.exponent) - (16383l + 112);
583             rop._get_num() = 1u;
584             rop._get_num() <<= 112u;
585             rop._get_num() += integer<SSize>{ief.i_eee.mant_high} << 64u;
586             rop._get_num() += ief.i_eee.mant_low;
587             if (exponent >= 0) {
588                 // The result is a large integer: no need to canonicalise or to try
589                 // to demote. Den is already set to 1.
590                 rop._get_num() <<= static_cast<unsigned long>(exponent);
591             } else {
592                 rop._get_den() <<= static_cast<unsigned long>(-exponent);
593                 // Put in canonical form.
594                 canonicalise(rop);
595                 // Try demoting, after having possibly removed common factors.
596                 rop._get_num().demote();
597                 rop._get_den().demote();
598             }
599         } else {
600             // Subnormal number.
601             rop._get_num() = ief.i_eee.mant_high;
602             rop._get_num() <<= 64u;
603             rop._get_num() += ief.i_eee.mant_low;
604             rop._get_den() <<= static_cast<unsigned long>(16382l + 112);
605             canonicalise(rop);
606             // Try demoting.
607             rop._get_num().demote();
608             rop._get_den().demote();
609         }
610         // Adjust the sign.
611         if (ief.i_eee.negative) {
612             rop.neg();
613         }
614         return true;
615     }
616 
617     // Conversion to mp++ types.
618     template <typename T>
619     MPPP_NODISCARD T dispatch_conversion(std::false_type) const
620     {
621         T retval;
622         if (mppp_unlikely(!mppp_conversion(retval))) {
623             throw std::domain_error(std::string{"Cannot convert a non-finite real128 to "}
624                                     + (detail::is_integer<T>::value ? "an integer" : "a rational"));
625         }
626         return retval;
627     }
628 
629 public:
630     // Conversion operator to interoperable types.
631 #if defined(MPPP_HAVE_CONCEPTS)
632     template <real128_interoperable T>
633 #else
634     template <typename T, detail::enable_if_t<is_real128_interoperable<T>::value, int> = 0>
635 #endif
636     constexpr explicit operator T() const
637     {
638         return dispatch_conversion<T>(detail::is_real128_cpp_interoperable<T>{});
639     }
640 
641     // Conversion operator to C++ complex types.
642 #if defined(MPPP_HAVE_CONCEPTS)
643     template <real128_cpp_complex T>
644 #else
645     template <typename T, detail::enable_if_t<is_real128_cpp_complex<T>::value, int> = 0>
646 #endif
647     MPPP_CONSTEXPR_14 explicit operator T() const
648     {
649         using value_t = typename T::value_type;
650 
651         return T{static_cast<value_t>(*this), value_t(0)};
652     }
653 
654 private:
655     // get() implementation for C++ types.
656     template <typename T>
657     MPPP_CONSTEXPR_14 bool dispatch_get(T &rop, std::true_type) const
658     {
659         return rop = static_cast<T>(m_value), true;
660     }
661     // get() implementation for mp++ types.
662     template <typename T>
663     bool dispatch_get(T &rop, std::false_type) const
664     {
665         return mppp_conversion(rop);
666     }
667 
668 public:
669     // Conversion member function to interoperable types.
670 #if defined(MPPP_HAVE_CONCEPTS)
671     template <real128_interoperable T>
672 #else
673     template <typename T, detail::enable_if_t<is_real128_interoperable<T>::value, int> = 0>
674 #endif
675     MPPP_CONSTEXPR_14 bool get(T &rop) const
676     {
677         return dispatch_get(rop, detail::is_real128_cpp_interoperable<T>{});
678     }
679 
680     // Conversion member function to C++ complex types.
681 #if defined(MPPP_HAVE_CONCEPTS)
682     template <real128_cpp_complex T>
683 #else
684     template <typename T, detail::enable_if_t<is_real128_cpp_complex<T>::value, int> = 0>
685 #endif
686     MPPP_CONSTEXPR_20 bool get(T &rop) const
687     {
688         using value_type = typename T::value_type;
689 
690         // NOTE: constexpr mutation of a std::complex
691         // seems to be available only since C++20:
692         // https://en.cppreference.com/w/cpp/numeric/complex/real
693         // https://en.cppreference.com/w/cpp/numeric/complex/operator%3D
694         rop.real(static_cast<value_type>(m_value));
695         rop.imag(static_cast<value_type>(0));
696 
697         return true;
698     }
699 
700     // Convert to string.
701     MPPP_NODISCARD std::string to_string() const;
702 
703     // Unbiased exponent.
704     MPPP_NODISCARD int ilogb() const;
705 #if defined(MPPP_QUADMATH_HAVE_LOGBQ)
706     MPPP_NODISCARD real128 logb() const;
707 #endif
708 
709     // Get the IEEE representation of the value.
710     MPPP_NODISCARD std::tuple<std::uint_least8_t, std::uint_least16_t, std::uint_least64_t, std::uint_least64_t>
711     get_ieee() const
712     {
713         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
714         detail::ieee_float128 ie;
715         ie.value = m_value;
716         return std::make_tuple(std::uint_least8_t(ie.i_eee.negative), std::uint_least16_t(ie.i_eee.exponent),
717                                std::uint_least64_t(ie.i_eee.mant_high), std::uint_least64_t(ie.i_eee.mant_low));
718     }
719     // Sign bit.
720     MPPP_NODISCARD bool signbit() const;
721     // Categorise the floating point value.
722     MPPP_NODISCARD
723 #if !defined(__INTEL_COMPILER)
724     constexpr
725 #endif
726         int
727         fpclassify() const
728     {
729         // NOTE: according to the docs the builtin accepts generic floating-point types:
730         // https://gcc.gnu.org/onlinedocs/gcc-7.2.0/gcc/Other-Builtins.html
731         // It is used internally in the quadmath library as well:
732         // https://github.com/gcc-mirror/gcc/blob/master/libquadmath/quadmath-imp.h
733         return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, m_value);
734     }
735     // Detect NaN.
736     MPPP_NODISCARD
737 #if !defined(__INTEL_COMPILER)
738     constexpr
739 #endif
740         bool
741         isnan() const
742     {
743         return fpclassify() == FP_NAN;
744     }
745     // Detect infinity.
746     MPPP_NODISCARD
747 #if !defined(__INTEL_COMPILER)
748     constexpr
749 #endif
750         bool
751         isinf() const
752     {
753         return fpclassify() == FP_INFINITE;
754     }
755     // Detect finite value.
756     MPPP_NODISCARD
757 #if !defined(__INTEL_COMPILER)
758     constexpr
759 #endif
760         bool
761         finite() const
762     {
763         return !isnan() && !isinf();
764     }
765     MPPP_NODISCARD
766 #if !defined(__INTEL_COMPILER)
767     constexpr
768 #endif
769         bool
770         isfinite() const
771     {
772         return finite();
773     }
774     // Detect normal value.
775     MPPP_NODISCARD
776 #if !defined(__INTEL_COMPILER)
777     constexpr
778 #endif
779         bool
780         isnormal() const
781     {
782         return fpclassify() == FP_NORMAL;
783     }
784 
785     // In-place absolute value.
786 #if !defined(__INTEL_COMPILER)
787     MPPP_CONSTEXPR_14
788 #endif
789     real128 &abs()
790     {
791         return *this = mppp::abs(*this);
792     }
793 #if !defined(__INTEL_COMPILER)
794     MPPP_CONSTEXPR_14
795 #endif
796     real128 &fabs()
797     {
798         return this->abs();
799     }
800 
801     // In-place roots.
802     real128 &sqrt();
803     real128 &cbrt();
804 
805     // In-place trigonometric functions.
806     real128 &sin();
807     real128 &cos();
808     real128 &tan();
809     real128 &asin();
810     real128 &acos();
811     real128 &atan();
812 
813     // In-place hyperbolic functions.
814     real128 &sinh();
815     real128 &cosh();
816     real128 &tanh();
817     real128 &asinh();
818     real128 &acosh();
819     real128 &atanh();
820 
821     // In-place exponentials and logarithms.
822     real128 &exp();
823 #if defined(MPPP_QUADMATH_HAVE_EXP2Q)
824     real128 &exp2();
825 #endif
826     real128 &expm1();
827     real128 &log();
828     real128 &log10();
829     real128 &log2();
830     real128 &log1p();
831 
832     // In-place gamma functions.
833     real128 &lgamma();
834     real128 &tgamma();
835 
836     // In-place Bessel functions.
837     real128 &j0();
838     real128 &j1();
839     real128 &y0();
840     real128 &y1();
841 
842     // In-place error functions.
843     real128 &erf();
844     real128 &erfc();
845 
846     // Integer and remainder-related functions.
847     real128 &ceil();
848     real128 &floor();
849     real128 &nearbyint();
850     real128 &rint();
851     real128 &round();
852     real128 &trunc();
853 
854     // The internal value.
855     // NOLINTNEXTLINE(modernize-use-default-member-init)
856     __float128 m_value;
857 };
858 
859 // Double check that real128 is a standard layout class.
860 static_assert(std::is_standard_layout<real128>::value, "real128 is not a standard layout class.");
861 
862 // Conversion function.
863 template <typename T>
get(T & rop,const real128 & x)864 inline MPPP_CONSTEXPR_14 auto get(T &rop, const real128 &x) -> decltype(x.get(rop))
865 {
866     return x.get(rop);
867 }
868 
869 // Decompose into a normalized fraction and an integral power of two.
870 MPPP_DLL_PUBLIC real128 frexp(const real128 &, int *);
871 
872 // Unbiased exponent.
873 MPPP_DLL_PUBLIC int ilogb(const real128 &);
874 #if defined(MPPP_QUADMATH_HAVE_LOGBQ)
875 MPPP_DLL_PUBLIC real128 logb(const real128 &);
876 #endif
877 
878 // Fused multiply-add.
879 MPPP_DLL_PUBLIC real128 fma(const real128 &, const real128 &, const real128 &);
880 
881 // Absolute value.
882 #if defined(__INTEL_COMPILER)
883 inline
884 #else
885 constexpr
886 #endif
887     real128
abs(const real128 & x)888     abs(const real128 &x)
889 {
890     return x.fpclassify() == FP_NAN
891                ? x
892                : (x.fpclassify() == FP_ZERO ? real128{} : (x.m_value < 0 ? real128{-x.m_value} : x));
893 }
894 
895 #if defined(__INTEL_COMPILER)
896 inline
897 #else
898 constexpr
899 #endif
900     real128
fabs(const real128 & x)901     fabs(const real128 &x)
902 {
903     return mppp::abs(x);
904 }
905 
906 // Multiply by power of 2.
907 MPPP_DLL_PUBLIC real128 scalbn(const real128 &, int);
908 MPPP_DLL_PUBLIC real128 scalbln(const real128 &, long);
909 MPPP_DLL_PUBLIC real128 ldexp(const real128 &, int);
910 
911 // Output stream operator.
912 MPPP_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const real128 &);
913 
914 // Sign bit.
signbit(const real128 & x)915 inline bool signbit(const real128 &x)
916 {
917     return x.signbit();
918 }
919 
920 // Categorisation.
921 #if defined(__INTEL_COMPILER)
922 inline
923 #else
924 constexpr
925 #endif
926     int
fpclassify(const real128 & x)927     fpclassify(const real128 &x)
928 {
929     return x.fpclassify();
930 }
931 
932 // Detect NaN.
933 #if defined(__INTEL_COMPILER)
934 inline
935 #else
936 constexpr
937 #endif
938     bool
isnan(const real128 & x)939     isnan(const real128 &x)
940 {
941     return x.isnan();
942 }
943 
944 // Detect infinity.
945 #if defined(__INTEL_COMPILER)
946 inline
947 #else
948 constexpr
949 #endif
950     bool
isinf(const real128 & x)951     isinf(const real128 &x)
952 {
953     return x.isinf();
954 }
955 
956 // Detect finite value.
957 #if defined(__INTEL_COMPILER)
958 inline
959 #else
960 constexpr
961 #endif
962     bool
finite(const real128 & x)963     finite(const real128 &x)
964 {
965     return x.finite();
966 }
967 
968 #if defined(__INTEL_COMPILER)
969 inline
970 #else
971 constexpr
972 #endif
973     bool
isfinite(const real128 & x)974     isfinite(const real128 &x)
975 {
976     return x.isfinite();
977 }
978 
979 // Detect normal value.
980 #if defined(__INTEL_COMPILER)
981 inline
982 #else
983 constexpr
984 #endif
985     bool
isnormal(const real128 & x)986     isnormal(const real128 &x)
987 {
988     return x.isnormal();
989 }
990 
991 // Equality predicate with special NaN handling.
992 #if !defined(__INTEL_COMPILER)
993 constexpr
994 #endif
995     bool
996     real128_equal_to(const real128 &, const real128 &);
997 
998 // Less-than predicate with special NaN handling.
999 #if !defined(__INTEL_COMPILER)
1000 constexpr
1001 #endif
1002     bool
1003     real128_lt(const real128 &, const real128 &);
1004 
1005 // Greater-than predicate with special NaN handling.
1006 #if !defined(__INTEL_COMPILER)
1007 constexpr
1008 #endif
1009     bool
1010     real128_gt(const real128 &, const real128 &);
1011 
1012 // Roots.
1013 MPPP_DLL_PUBLIC real128 sqrt(const real128 &);
1014 MPPP_DLL_PUBLIC real128 cbrt(const real128 &);
1015 
1016 // Machinery to define binary operations involving real128.
1017 #if defined(MPPP_HAVE_CONCEPTS)
1018 #define MPPP_REAL128_BINARY_OP_HEADER                                                                                  \
1019     template <typename T, typename U>                                                                                  \
1020     requires real128_op_types<T, U>
1021 #else
1022 #define MPPP_REAL128_BINARY_OP_HEADER                                                                                  \
1023     template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1024 #endif
1025 
1026 #define MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(fname)                                                                 \
1027     namespace detail                                                                                                   \
1028     {                                                                                                                  \
1029     MPPP_DLL_PUBLIC real128 dispatch_real128_##fname(const real128 &, const real128 &);                                \
1030     template <typename T>                                                                                              \
1031     inline real128 dispatch_real128_##fname(const real128 &x, const T &y)                                              \
1032     {                                                                                                                  \
1033         return dispatch_real128_##fname(x, real128{y});                                                                \
1034     }                                                                                                                  \
1035     template <typename T>                                                                                              \
1036     inline real128 dispatch_real128_##fname(const T &x, const real128 &y)                                              \
1037     {                                                                                                                  \
1038         return dispatch_real128_##fname(real128{x}, y);                                                                \
1039     }                                                                                                                  \
1040     }                                                                                                                  \
1041     MPPP_REAL128_BINARY_OP_HEADER                                                                                      \
1042     inline real128 fname(const T &x, const U &y)                                                                       \
1043     {                                                                                                                  \
1044         return detail::dispatch_real128_##fname(x, y);                                                                 \
1045     }
1046 
1047 // Euclidean distance.
1048 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(hypot)
1049 
1050 // Exponentiation.
1051 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(pow)
1052 
1053 // Logarithms and exponentials.
1054 MPPP_DLL_PUBLIC real128 exp(const real128 &);
1055 #if defined(MPPP_QUADMATH_HAVE_EXP2Q)
1056 MPPP_DLL_PUBLIC real128 exp2(const real128 &);
1057 #endif
1058 MPPP_DLL_PUBLIC real128 expm1(const real128 &);
1059 MPPP_DLL_PUBLIC real128 log(const real128 &);
1060 MPPP_DLL_PUBLIC real128 log10(const real128 &);
1061 MPPP_DLL_PUBLIC real128 log2(const real128 &);
1062 MPPP_DLL_PUBLIC real128 log1p(const real128 &);
1063 
1064 // Trigonometric functions.
1065 MPPP_DLL_PUBLIC real128 sin(const real128 &);
1066 MPPP_DLL_PUBLIC real128 cos(const real128 &);
1067 MPPP_DLL_PUBLIC real128 tan(const real128 &);
1068 MPPP_DLL_PUBLIC real128 asin(const real128 &);
1069 MPPP_DLL_PUBLIC real128 acos(const real128 &);
1070 MPPP_DLL_PUBLIC real128 atan(const real128 &);
1071 
1072 // atan2.
1073 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(atan2)
1074 
1075 // Sine and cosine at the same time.
1076 MPPP_DLL_PUBLIC void sincos(const real128 &, real128 *, real128 *);
1077 
1078 // Hyperbolic functions.
1079 MPPP_DLL_PUBLIC real128 sinh(const real128 &);
1080 MPPP_DLL_PUBLIC real128 cosh(const real128 &);
1081 MPPP_DLL_PUBLIC real128 tanh(const real128 &);
1082 MPPP_DLL_PUBLIC real128 asinh(const real128 &);
1083 MPPP_DLL_PUBLIC real128 acosh(const real128 &);
1084 MPPP_DLL_PUBLIC real128 atanh(const real128 &);
1085 
1086 // Gamma functions.
1087 MPPP_DLL_PUBLIC real128 lgamma(const real128 &);
1088 MPPP_DLL_PUBLIC real128 tgamma(const real128 &);
1089 
1090 // Bessel functions.
1091 MPPP_DLL_PUBLIC real128 j0(const real128 &);
1092 MPPP_DLL_PUBLIC real128 j1(const real128 &);
1093 MPPP_DLL_PUBLIC real128 y0(const real128 &);
1094 MPPP_DLL_PUBLIC real128 y1(const real128 &);
1095 
1096 MPPP_DLL_PUBLIC real128 jn(int, const real128 &);
1097 MPPP_DLL_PUBLIC real128 yn(int, const real128 &);
1098 
1099 // Error functions.
1100 MPPP_DLL_PUBLIC real128 erf(const real128 &);
1101 MPPP_DLL_PUBLIC real128 erfc(const real128 &);
1102 
1103 // Next real128 from 'from' to 'to'.
1104 MPPP_DLL_PUBLIC real128 nextafter(const real128 &, const real128 &);
1105 
1106 // Copy sign.
1107 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(copysign)
1108 
1109 // fdim.
1110 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(fdim)
1111 
1112 // fmax/fmin.
1113 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(fmax)
1114 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(fmin)
1115 
1116 // Integer and remainder-related functions.
1117 MPPP_DLL_PUBLIC real128 ceil(const real128 &);
1118 MPPP_DLL_PUBLIC real128 floor(const real128 &);
1119 MPPP_DLL_PUBLIC real128 nearbyint(const real128 &);
1120 MPPP_DLL_PUBLIC real128 rint(const real128 &);
1121 MPPP_DLL_PUBLIC real128 round(const real128 &);
1122 MPPP_DLL_PUBLIC real128 trunc(const real128 &);
1123 MPPP_DLL_PUBLIC long long llrint(const real128 &);
1124 MPPP_DLL_PUBLIC long lrint(const real128 &);
1125 MPPP_DLL_PUBLIC long long llround(const real128 &);
1126 MPPP_DLL_PUBLIC long lround(const real128 &);
1127 
1128 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(fmod)
1129 MPPP_REAL128_IMPLEMENT_BINARY_OPERATION(remainder)
1130 
1131 MPPP_DLL_PUBLIC real128 modf(const real128 &, real128 *);
1132 MPPP_DLL_PUBLIC real128 remquo(const real128 &, const real128 &, int *);
1133 
1134 #undef MPPP_REAL128_BINARY_OP_HEADER
1135 #undef MPPP_REAL128_IMPLEMENT_BINARY_OPERATION
1136 
1137 // Identity operator.
operator +(const real128 & x)1138 constexpr real128 operator+(const real128 &x)
1139 {
1140     return x;
1141 }
1142 
1143 namespace detail
1144 {
1145 
dispatch_add(const real128 & x,const real128 & y)1146 constexpr real128 dispatch_add(const real128 &x, const real128 &y)
1147 {
1148     return real128{x.m_value + y.m_value};
1149 }
1150 
1151 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_add(const real128 & x,const T & y)1152 constexpr real128 dispatch_add(const real128 &x, const T &y)
1153 {
1154     return real128{x.m_value + y};
1155 }
1156 
1157 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_add(const T & x,const real128 & y)1158 constexpr real128 dispatch_add(const T &x, const real128 &y)
1159 {
1160     return real128{x + y.m_value};
1161 }
1162 
1163 // NOTE: dispatching for mp++ types needs to be declared here
1164 // but implemented below, as it needs the presence of an operator+()
1165 // for real128.
1166 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1167 real128 dispatch_add(const real128 &, const T &);
1168 
1169 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1170 real128 dispatch_add(const T &, const real128 &);
1171 
1172 } // namespace detail
1173 
1174 // Binary addition.
1175 #if defined(MPPP_HAVE_CONCEPTS)
1176 template <typename T, typename U>
1177 requires real128_op_types<T, U>
1178 #else
1179 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1180 #endif
operator +(const T & x,const U & y)1181 constexpr real128 operator+(const T &x, const U &y)
1182 {
1183     return detail::dispatch_add(x, y);
1184 }
1185 
1186 namespace detail
1187 {
1188 
1189 // Definitions for the binary dispatch functions above.
1190 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_add(const real128 & x,const T & y)1191 inline real128 dispatch_add(const real128 &x, const T &y)
1192 {
1193     return x + real128{y};
1194 }
1195 
1196 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_add(const T & x,const real128 & y)1197 inline real128 dispatch_add(const T &x, const real128 &y)
1198 {
1199     return real128{x} + y;
1200 }
1201 
1202 // NOTE: we need the MPPP_CONSTEXPR_14 construct in the implementation detail as well,
1203 // as returning void in a constexpr function is not allowed in C++11.
dispatch_in_place_add(real128 & x,const real128 & y)1204 inline MPPP_CONSTEXPR_14 void dispatch_in_place_add(real128 &x, const real128 &y)
1205 {
1206     x.m_value += y.m_value;
1207 }
1208 
1209 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_add(real128 & x,const T & y)1210 inline MPPP_CONSTEXPR_14 void dispatch_in_place_add(real128 &x, const T &y)
1211 {
1212     x.m_value += y;
1213 }
1214 
1215 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_add(T & x,const real128 & y)1216 inline MPPP_CONSTEXPR_14 void dispatch_in_place_add(T &x, const real128 &y)
1217 {
1218     x = static_cast<T>(x + y.m_value);
1219 }
1220 
1221 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_add(real128 & x,const T & y)1222 inline void dispatch_in_place_add(real128 &x, const T &y)
1223 {
1224     x = x + y;
1225 }
1226 
1227 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_add(T & x,const real128 & y)1228 inline void dispatch_in_place_add(T &x, const real128 &y)
1229 {
1230     x = static_cast<T>(x + y);
1231 }
1232 
1233 } // namespace detail
1234 
1235 // In-place addition.
1236 #if defined(MPPP_HAVE_CONCEPTS)
1237 template <typename T, typename U>
1238 requires real128_op_types<T, U>
1239 #else
1240 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1241 #endif
operator +=(T & x,const U & y)1242 inline MPPP_CONSTEXPR_14 T &operator+=(T &x, const U &y)
1243 {
1244     detail::dispatch_in_place_add(x, y);
1245     return x;
1246 }
1247 
1248 // Prefix increment.
operator ++(real128 & x)1249 inline MPPP_CONSTEXPR_14 real128 &operator++(real128 &x)
1250 {
1251     x.m_value += 1;
1252     return x;
1253 }
1254 
1255 // Suffix increment.
operator ++(real128 & x,int)1256 inline MPPP_CONSTEXPR_14 real128 operator++(real128 &x, int)
1257 {
1258     auto retval(x);
1259     ++x;
1260     return retval;
1261 }
1262 
1263 // Negation operator.
operator -(const real128 & x)1264 constexpr real128 operator-(const real128 &x)
1265 {
1266     return real128{-x.m_value};
1267 }
1268 
1269 namespace detail
1270 {
1271 
dispatch_sub(const real128 & x,const real128 & y)1272 constexpr real128 dispatch_sub(const real128 &x, const real128 &y)
1273 {
1274     return real128{x.m_value - y.m_value};
1275 }
1276 
1277 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_sub(const real128 & x,const T & y)1278 constexpr real128 dispatch_sub(const real128 &x, const T &y)
1279 {
1280     return real128{x.m_value - y};
1281 }
1282 
1283 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_sub(const T & x,const real128 & y)1284 constexpr real128 dispatch_sub(const T &x, const real128 &y)
1285 {
1286     return real128{x - y.m_value};
1287 }
1288 
1289 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1290 real128 dispatch_sub(const real128 &, const T &);
1291 
1292 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1293 real128 dispatch_sub(const T &, const real128 &);
1294 
1295 } // namespace detail
1296 
1297 // Binary subtraction.
1298 #if defined(MPPP_HAVE_CONCEPTS)
1299 template <typename T, typename U>
1300 requires real128_op_types<T, U>
1301 #else
1302 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1303 #endif
operator -(const T & x,const U & y)1304 constexpr real128 operator-(const T &x, const U &y)
1305 {
1306     return detail::dispatch_sub(x, y);
1307 }
1308 
1309 namespace detail
1310 {
1311 
1312 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_sub(const real128 & x,const T & y)1313 inline real128 dispatch_sub(const real128 &x, const T &y)
1314 {
1315     return x - real128{y};
1316 }
1317 
1318 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_sub(const T & x,const real128 & y)1319 inline real128 dispatch_sub(const T &x, const real128 &y)
1320 {
1321     return real128{x} - y;
1322 }
1323 
dispatch_in_place_sub(real128 & x,const real128 & y)1324 inline MPPP_CONSTEXPR_14 void dispatch_in_place_sub(real128 &x, const real128 &y)
1325 {
1326     x.m_value -= y.m_value;
1327 }
1328 
1329 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_sub(real128 & x,const T & y)1330 inline MPPP_CONSTEXPR_14 void dispatch_in_place_sub(real128 &x, const T &y)
1331 {
1332     x.m_value -= y;
1333 }
1334 
1335 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_sub(T & x,const real128 & y)1336 inline MPPP_CONSTEXPR_14 void dispatch_in_place_sub(T &x, const real128 &y)
1337 {
1338     x = static_cast<T>(x - y.m_value);
1339 }
1340 
1341 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_sub(real128 & x,const T & y)1342 inline void dispatch_in_place_sub(real128 &x, const T &y)
1343 {
1344     x = x - y;
1345 }
1346 
1347 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_sub(T & x,const real128 & y)1348 inline void dispatch_in_place_sub(T &x, const real128 &y)
1349 {
1350     x = static_cast<T>(x - y);
1351 }
1352 
1353 } // namespace detail
1354 
1355 // In-place subtraction.
1356 #if defined(MPPP_HAVE_CONCEPTS)
1357 template <typename T, typename U>
1358 requires real128_op_types<T, U>
1359 #else
1360 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1361 #endif
operator -=(T & x,const U & y)1362 inline MPPP_CONSTEXPR_14 T &operator-=(T &x, const U &y)
1363 {
1364     detail::dispatch_in_place_sub(x, y);
1365     return x;
1366 }
1367 
1368 // Prefix decrement.
operator --(real128 & x)1369 inline MPPP_CONSTEXPR_14 real128 &operator--(real128 &x)
1370 {
1371     x.m_value -= 1;
1372     return x;
1373 }
1374 
1375 // Suffix decrement.
operator --(real128 & x,int)1376 inline MPPP_CONSTEXPR_14 real128 operator--(real128 &x, int)
1377 {
1378     auto retval(x);
1379     --x;
1380     return retval;
1381 }
1382 
1383 namespace detail
1384 {
1385 
dispatch_mul(const real128 & x,const real128 & y)1386 constexpr real128 dispatch_mul(const real128 &x, const real128 &y)
1387 {
1388     return real128{x.m_value * y.m_value};
1389 }
1390 
1391 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_mul(const real128 & x,const T & y)1392 constexpr real128 dispatch_mul(const real128 &x, const T &y)
1393 {
1394     return real128{x.m_value * y};
1395 }
1396 
1397 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_mul(const T & x,const real128 & y)1398 constexpr real128 dispatch_mul(const T &x, const real128 &y)
1399 {
1400     return real128{x * y.m_value};
1401 }
1402 
1403 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1404 real128 dispatch_mul(const real128 &, const T &);
1405 
1406 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1407 real128 dispatch_mul(const T &, const real128 &);
1408 
1409 } // namespace detail
1410 
1411 // Binary multiplication.
1412 #if defined(MPPP_HAVE_CONCEPTS)
1413 template <typename T, typename U>
1414 requires real128_op_types<T, U>
1415 #else
1416 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1417 #endif
operator *(const T & x,const U & y)1418 constexpr real128 operator*(const T &x, const U &y)
1419 {
1420     return detail::dispatch_mul(x, y);
1421 }
1422 
1423 namespace detail
1424 {
1425 
1426 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_mul(const real128 & x,const T & y)1427 inline real128 dispatch_mul(const real128 &x, const T &y)
1428 {
1429     return x * real128{y};
1430 }
1431 
1432 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_mul(const T & x,const real128 & y)1433 inline real128 dispatch_mul(const T &x, const real128 &y)
1434 {
1435     return real128{x} * y;
1436 }
1437 
dispatch_in_place_mul(real128 & x,const real128 & y)1438 inline MPPP_CONSTEXPR_14 void dispatch_in_place_mul(real128 &x, const real128 &y)
1439 {
1440     x.m_value *= y.m_value;
1441 }
1442 
1443 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_mul(real128 & x,const T & y)1444 inline MPPP_CONSTEXPR_14 void dispatch_in_place_mul(real128 &x, const T &y)
1445 {
1446     x.m_value *= y;
1447 }
1448 
1449 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_mul(T & x,const real128 & y)1450 inline MPPP_CONSTEXPR_14 void dispatch_in_place_mul(T &x, const real128 &y)
1451 {
1452     x = static_cast<T>(x * y.m_value);
1453 }
1454 
1455 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_mul(real128 & x,const T & y)1456 inline void dispatch_in_place_mul(real128 &x, const T &y)
1457 {
1458     x = x * y;
1459 }
1460 
1461 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_mul(T & x,const real128 & y)1462 inline void dispatch_in_place_mul(T &x, const real128 &y)
1463 {
1464     x = static_cast<T>(x * y);
1465 }
1466 
1467 } // namespace detail
1468 
1469 // In-place multiplication.
1470 #if defined(MPPP_HAVE_CONCEPTS)
1471 template <typename T, typename U>
1472 requires real128_op_types<T, U>
1473 #else
1474 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1475 #endif
operator *=(T & x,const U & y)1476 inline MPPP_CONSTEXPR_14 T &operator*=(T &x, const U &y)
1477 {
1478     detail::dispatch_in_place_mul(x, y);
1479     return x;
1480 }
1481 
1482 namespace detail
1483 {
1484 
dispatch_div(const real128 & x,const real128 & y)1485 constexpr real128 dispatch_div(const real128 &x, const real128 &y)
1486 {
1487     return real128{x.m_value / y.m_value};
1488 }
1489 
1490 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_div(const real128 & x,const T & y)1491 constexpr real128 dispatch_div(const real128 &x, const T &y)
1492 {
1493     return real128{x.m_value / y};
1494 }
1495 
1496 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_div(const T & x,const real128 & y)1497 constexpr real128 dispatch_div(const T &x, const real128 &y)
1498 {
1499     return real128{x / y.m_value};
1500 }
1501 
1502 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1503 real128 dispatch_div(const real128 &, const T &);
1504 
1505 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1506 real128 dispatch_div(const T &, const real128 &);
1507 
1508 } // namespace detail
1509 
1510 // Binary division.
1511 #if defined(MPPP_HAVE_CONCEPTS)
1512 template <typename T, typename U>
1513 requires real128_op_types<T, U>
1514 #else
1515 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1516 #endif
operator /(const T & x,const U & y)1517 constexpr real128 operator/(const T &x, const U &y)
1518 {
1519     return detail::dispatch_div(x, y);
1520 }
1521 
1522 namespace detail
1523 {
1524 
1525 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_div(const real128 & x,const T & y)1526 inline real128 dispatch_div(const real128 &x, const T &y)
1527 {
1528     return x / real128{y};
1529 }
1530 
1531 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_div(const T & x,const real128 & y)1532 inline real128 dispatch_div(const T &x, const real128 &y)
1533 {
1534     return real128{x} / y;
1535 }
1536 
dispatch_in_place_div(real128 & x,const real128 & y)1537 inline MPPP_CONSTEXPR_14 void dispatch_in_place_div(real128 &x, const real128 &y)
1538 {
1539     x.m_value /= y.m_value;
1540 }
1541 
1542 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_div(real128 & x,const T & y)1543 inline MPPP_CONSTEXPR_14 void dispatch_in_place_div(real128 &x, const T &y)
1544 {
1545     x.m_value /= y;
1546 }
1547 
1548 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_in_place_div(T & x,const real128 & y)1549 inline MPPP_CONSTEXPR_14 void dispatch_in_place_div(T &x, const real128 &y)
1550 {
1551     x = static_cast<T>(x / y.m_value);
1552 }
1553 
1554 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_div(real128 & x,const T & y)1555 inline void dispatch_in_place_div(real128 &x, const T &y)
1556 {
1557     x = x / y;
1558 }
1559 
1560 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
dispatch_in_place_div(T & x,const real128 & y)1561 inline void dispatch_in_place_div(T &x, const real128 &y)
1562 {
1563     x = static_cast<T>(x / y);
1564 }
1565 
1566 } // namespace detail
1567 
1568 // In-place division.
1569 #if defined(MPPP_HAVE_CONCEPTS)
1570 template <typename T, typename U>
1571 requires real128_op_types<T, U>
1572 #else
1573 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1574 #endif
operator /=(T & x,const U & y)1575 inline MPPP_CONSTEXPR_14 T &operator/=(T &x, const U &y)
1576 {
1577     detail::dispatch_in_place_div(x, y);
1578     return x;
1579 }
1580 
1581 template <typename T, typename U>
1582 using are_real128_eq_op_types
1583     = detail::disjunction<are_real128_op_types<T, U>,
1584                           detail::conjunction<std::is_same<T, real128>, is_real128_cpp_complex<U>>,
1585                           detail::conjunction<std::is_same<U, real128>, is_real128_cpp_complex<T>>>;
1586 
1587 #if defined(MPPP_HAVE_CONCEPTS)
1588 
1589 template <typename T, typename U>
1590 MPPP_CONCEPT_DECL real128_eq_op_types = are_real128_eq_op_types<T, U>::value;
1591 
1592 #endif
1593 
1594 namespace detail
1595 {
1596 
dispatch_eq(const real128 & x,const real128 & y)1597 constexpr bool dispatch_eq(const real128 &x, const real128 &y)
1598 {
1599     return x.m_value == y.m_value;
1600 }
1601 
1602 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_eq(const real128 & x,const T & y)1603 constexpr bool dispatch_eq(const real128 &x, const T &y)
1604 {
1605     return x.m_value == y;
1606 }
1607 
1608 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_eq(const T & x,const real128 & y)1609 constexpr bool dispatch_eq(const T &x, const real128 &y)
1610 {
1611     return x == y.m_value;
1612 }
1613 
1614 template <typename T, enable_if_t<is_real128_cpp_complex<T>::value, int> = 0>
dispatch_eq(const real128 & x,const T & y)1615 constexpr bool dispatch_eq(const real128 &x, const T &y)
1616 {
1617     // NOTE: follow what std::complex does here, that is, real arguments are treated as
1618     // complex numbers with the real part equal to the argument and the imaginary part set to zero.
1619     // https://en.cppreference.com/w/cpp/numeric/complex/operator_cmp
1620     return y.imag() == 0 && y.real() == x.m_value;
1621 }
1622 
1623 template <typename T, enable_if_t<is_real128_cpp_complex<T>::value, int> = 0>
dispatch_eq(const T & x,const real128 & y)1624 constexpr bool dispatch_eq(const T &x, const real128 &y)
1625 {
1626     return dispatch_eq(y, x);
1627 }
1628 
1629 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1630 bool dispatch_eq(const real128 &, const T &);
1631 
1632 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1633 bool dispatch_eq(const T &, const real128 &);
1634 
1635 } // namespace detail
1636 
1637 // Equality operator.
1638 #if defined(MPPP_HAVE_CONCEPTS)
1639 template <typename T, typename U>
1640 requires real128_eq_op_types<T, U>
1641 #else
1642 template <typename T, typename U, detail::enable_if_t<are_real128_eq_op_types<T, U>::value, int> = 0>
1643 #endif
operator ==(const T & x,const U & y)1644 constexpr bool operator==(const T &x, const U &y)
1645 {
1646     return detail::dispatch_eq(x, y);
1647 }
1648 
1649 namespace detail
1650 {
1651 
1652 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_eq(const real128 & x,const T & y)1653 inline bool dispatch_eq(const real128 &x, const T &y)
1654 {
1655     return x == real128{y};
1656 }
1657 
1658 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_eq(const T & x,const real128 & y)1659 inline bool dispatch_eq(const T &x, const real128 &y)
1660 {
1661     return real128{x} == y;
1662 }
1663 
1664 } // namespace detail
1665 
1666 // Inequality operator.
1667 #if defined(MPPP_HAVE_CONCEPTS)
1668 template <typename T, typename U>
1669 requires real128_eq_op_types<T, U>
1670 #else
1671 template <typename T, typename U, detail::enable_if_t<are_real128_eq_op_types<T, U>::value, int> = 0>
1672 #endif
operator !=(const T & x,const U & y)1673 constexpr bool operator!=(const T &x, const U &y)
1674 {
1675     return !(x == y);
1676 }
1677 
1678 namespace detail
1679 {
1680 
dispatch_lt(const real128 & x,const real128 & y)1681 constexpr bool dispatch_lt(const real128 &x, const real128 &y)
1682 {
1683     return x.m_value < y.m_value;
1684 }
1685 
1686 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_lt(const real128 & x,const T & y)1687 constexpr bool dispatch_lt(const real128 &x, const T &y)
1688 {
1689     return x.m_value < y;
1690 }
1691 
1692 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_lt(const T & x,const real128 & y)1693 constexpr bool dispatch_lt(const T &x, const real128 &y)
1694 {
1695     return x < y.m_value;
1696 }
1697 
1698 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1699 bool dispatch_lt(const real128 &, const T &);
1700 
1701 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1702 bool dispatch_lt(const T &, const real128 &);
1703 
1704 } // namespace detail
1705 
1706 // Less-than operator.
1707 #if defined(MPPP_HAVE_CONCEPTS)
1708 template <typename T, typename U>
1709 requires real128_op_types<T, U>
1710 #else
1711 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1712 #endif
operator <(const T & x,const U & y)1713 constexpr bool operator<(const T &x, const U &y)
1714 {
1715     return detail::dispatch_lt(x, y);
1716 }
1717 
1718 namespace detail
1719 {
1720 
1721 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_lt(const real128 & x,const T & y)1722 inline bool dispatch_lt(const real128 &x, const T &y)
1723 {
1724     return x < real128{y};
1725 }
1726 
1727 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_lt(const T & x,const real128 & y)1728 inline bool dispatch_lt(const T &x, const real128 &y)
1729 {
1730     return real128{x} < y;
1731 }
1732 
dispatch_lte(const real128 & x,const real128 & y)1733 constexpr bool dispatch_lte(const real128 &x, const real128 &y)
1734 {
1735     return x.m_value <= y.m_value;
1736 }
1737 
1738 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_lte(const real128 & x,const T & y)1739 constexpr bool dispatch_lte(const real128 &x, const T &y)
1740 {
1741     return x.m_value <= y;
1742 }
1743 
1744 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_lte(const T & x,const real128 & y)1745 constexpr bool dispatch_lte(const T &x, const real128 &y)
1746 {
1747     return x <= y.m_value;
1748 }
1749 
1750 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1751 bool dispatch_lte(const real128 &, const T &);
1752 
1753 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1754 bool dispatch_lte(const T &, const real128 &);
1755 
1756 } // namespace detail
1757 
1758 // Less-than or equal operator.
1759 #if defined(MPPP_HAVE_CONCEPTS)
1760 template <typename T, typename U>
1761 requires real128_op_types<T, U>
1762 #else
1763 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1764 #endif
operator <=(const T & x,const U & y)1765 constexpr bool operator<=(const T &x, const U &y)
1766 {
1767     return detail::dispatch_lte(x, y);
1768 }
1769 
1770 namespace detail
1771 {
1772 
1773 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_lte(const real128 & x,const T & y)1774 inline bool dispatch_lte(const real128 &x, const T &y)
1775 {
1776     return x <= real128{y};
1777 }
1778 
1779 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_lte(const T & x,const real128 & y)1780 inline bool dispatch_lte(const T &x, const real128 &y)
1781 {
1782     return real128{x} <= y;
1783 }
1784 
dispatch_gt(const real128 & x,const real128 & y)1785 constexpr bool dispatch_gt(const real128 &x, const real128 &y)
1786 {
1787     return x.m_value > y.m_value;
1788 }
1789 
1790 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_gt(const real128 & x,const T & y)1791 constexpr bool dispatch_gt(const real128 &x, const T &y)
1792 {
1793     return x.m_value > y;
1794 }
1795 
1796 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_gt(const T & x,const real128 & y)1797 constexpr bool dispatch_gt(const T &x, const real128 &y)
1798 {
1799     return x > y.m_value;
1800 }
1801 
1802 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1803 bool dispatch_gt(const real128 &, const T &);
1804 
1805 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1806 bool dispatch_gt(const T &, const real128 &);
1807 
1808 } // namespace detail
1809 
1810 // Greater-than operator.
1811 #if defined(MPPP_HAVE_CONCEPTS)
1812 template <typename T, typename U>
1813 requires real128_op_types<T, U>
1814 #else
1815 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1816 #endif
operator >(const T & x,const U & y)1817 constexpr bool operator>(const T &x, const U &y)
1818 {
1819     return detail::dispatch_gt(x, y);
1820 }
1821 
1822 namespace detail
1823 {
1824 
1825 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_gt(const real128 & x,const T & y)1826 inline bool dispatch_gt(const real128 &x, const T &y)
1827 {
1828     return x > real128{y};
1829 }
1830 
1831 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_gt(const T & x,const real128 & y)1832 inline bool dispatch_gt(const T &x, const real128 &y)
1833 {
1834     return real128{x} > y;
1835 }
1836 
dispatch_gte(const real128 & x,const real128 & y)1837 constexpr bool dispatch_gte(const real128 &x, const real128 &y)
1838 {
1839     return x.m_value >= y.m_value;
1840 }
1841 
1842 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_gte(const real128 & x,const T & y)1843 constexpr bool dispatch_gte(const real128 &x, const T &y)
1844 {
1845     return x.m_value >= y;
1846 }
1847 
1848 template <typename T, enable_if_t<is_real128_cpp_interoperable<T>::value, int> = 0>
dispatch_gte(const T & x,const real128 & y)1849 constexpr bool dispatch_gte(const T &x, const real128 &y)
1850 {
1851     return x >= y.m_value;
1852 }
1853 
1854 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1855 bool dispatch_gte(const real128 &, const T &);
1856 
1857 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int> = 0>
1858 bool dispatch_gte(const T &, const real128 &);
1859 
1860 } // namespace detail
1861 
1862 // Greater-than or equal operator.
1863 #if defined(MPPP_HAVE_CONCEPTS)
1864 template <typename T, typename U>
1865 requires real128_op_types<T, U>
1866 #else
1867 template <typename T, typename U, detail::enable_if_t<are_real128_op_types<T, U>::value, int> = 0>
1868 #endif
operator >=(const T & x,const U & y)1869 constexpr bool operator>=(const T &x, const U &y)
1870 {
1871     return detail::dispatch_gte(x, y);
1872 }
1873 
1874 namespace detail
1875 {
1876 
1877 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_gte(const real128 & x,const T & y)1878 inline bool dispatch_gte(const real128 &x, const T &y)
1879 {
1880     return x >= real128{y};
1881 }
1882 
1883 template <typename T, enable_if_t<is_real128_mppp_interoperable<T>::value, int>>
dispatch_gte(const T & x,const real128 & y)1884 inline bool dispatch_gte(const T &x, const real128 &y)
1885 {
1886     return real128{x} >= y;
1887 }
1888 
1889 // Functions useful for the implementation of real128 constants below.
two_112()1890 constexpr real128 two_112()
1891 {
1892     return real128{1} / (1ull << 32) / (1ull << 32) / (1ull << 48);
1893 }
1894 
two_48()1895 constexpr real128 two_48()
1896 {
1897     return real128{1} / (1ull << 48);
1898 }
1899 
1900 // Recursively calculate 2**N, where N is a power of two greater than 32.
1901 template <unsigned long N>
two_ptwo()1902 constexpr real128 two_ptwo()
1903 {
1904     static_assert(N > 32u && !(N & (N - 1u)), "Invalid value for N.");
1905     return two_ptwo<N / 2u>() * two_ptwo<N / 2u>();
1906 }
1907 
1908 template <>
two_ptwo()1909 constexpr real128 two_ptwo<32ul>()
1910 {
1911     return real128{1ull << 32};
1912 }
1913 } // namespace detail
1914 
1915 // The number of binary digits in the significand.
real128_sig_digits()1916 constexpr unsigned real128_sig_digits()
1917 {
1918     return 113u;
1919 }
1920 
1921 // The largest positive finite value.
real128_max()1922 constexpr real128 real128_max()
1923 {
1924     return (18446744073709551615ull * detail::two_112() + 281474976710655ull * detail::two_48() + 1)
1925            * detail::two_ptwo<8192>() * detail::two_ptwo<4096>() * detail::two_ptwo<2048>() * detail::two_ptwo<1024>()
1926            * detail::two_ptwo<512>() * detail::two_ptwo<256>() * detail::two_ptwo<128>() * detail::two_ptwo<64>()
1927            * detail::two_ptwo<32>() * (1ull << 31);
1928 }
1929 
1930 // The smallest positive value.
real128_min()1931 constexpr real128 real128_min()
1932 {
1933     return 1 / detail::two_ptwo<8192>() / detail::two_ptwo<4096>() / detail::two_ptwo<2048>() / detail::two_ptwo<1024>()
1934            / detail::two_ptwo<512>() / detail::two_ptwo<256>() / detail::two_ptwo<128>() / detail::two_ptwo<64>()
1935            / detail::two_ptwo<32>() / (1ull << 30);
1936 }
1937 
1938 // The difference between 1 and the next larger number.
real128_epsilon()1939 constexpr real128 real128_epsilon()
1940 {
1941     return 1 / detail::two_ptwo<64>() / detail::two_ptwo<32>() / (1ull << 16);
1942 }
1943 
1944 // The smallest positive denormalized number.
real128_denorm_min()1945 constexpr real128 real128_denorm_min()
1946 {
1947     return 1 / detail::two_ptwo<8192>() / detail::two_ptwo<8192>() / detail::two_ptwo<64>() / (1ull << 46);
1948 }
1949 
1950 // Positive inf.
real128_inf()1951 constexpr real128 real128_inf()
1952 {
1953 #if defined(__INTEL_COMPILER) || !defined(__GNUC__) || __GNUC__ < 7
1954     // NOTE: it seems like there's no way to arithmetically construct infinity in constexpr.
1955     // I tried 1/0 and repeated multiplications by a large int, but it always ends up in
1956     // a 'not a constant expression' error message.
1957     return real128{std::numeric_limits<double>::infinity()};
1958 #else
1959     // This builtin is constexpr only on GCC 7 and later.
1960     // https://gcc.gnu.org/onlinedocs/gcc/x86-Built-in-Functions.html
1961     // Note that this and the nan builtins are arch-specific, but it seems they
1962     // might be available everywhere __float128 is available.
1963     return real128{__builtin_infq()};
1964 #endif
1965 }
1966 
1967 // NaN.
real128_nan()1968 constexpr real128 real128_nan()
1969 {
1970 #if defined(__INTEL_COMPILER) || !defined(__GNUC__) || __GNUC__ < 7
1971     // Same as above - GCC would accept arithmetic generation of NaN,
1972     // but Clang does not.
1973     return real128{std::numeric_limits<double>::quiet_NaN()};
1974 #else
1975     // This builtin is constexpr only on GCC 7 and later.
1976     return real128{__builtin_nanq("")};
1977 #endif
1978 }
1979 
1980 // Pi.
real128_pi()1981 constexpr real128 real128_pi()
1982 {
1983     return 2 * (9541308523256152504ull * detail::two_112() + 160664882791121ull * detail::two_48() + 1);
1984 }
1985 
1986 // Euler's number.
real128_e()1987 constexpr real128 real128_e()
1988 {
1989     return 2 * (10751604932185443962ull * detail::two_112() + 101089180468598ull * detail::two_48() + 1);
1990 }
1991 
1992 // Sqrt2.
real128_sqrt2()1993 constexpr real128 real128_sqrt2()
1994 {
1995     return 14486024992869247637ull * detail::two_112() + 116590752822204ull * detail::two_48() + 1;
1996 }
1997 
1998 #if MPPP_CPLUSPLUS >= 201703L
1999 
2000 // NOTE: namespace scope constexpr variables are *not* implicitly inline, so we need
2001 // inline explicitly here:
2002 // http://en.cppreference.com/w/cpp/language/inline
2003 // Note that constexpr static member variables are implicitly inline instead.
2004 
2005 // The number of binary digits in the significand.
2006 inline constexpr unsigned sig_digits_128 = real128_sig_digits();
2007 
2008 // The largest positive finite value.
2009 inline constexpr real128 max_128 = real128_max();
2010 
2011 // The smallest positive value representable with full precision.
2012 inline constexpr real128 min_128 = real128_min();
2013 
2014 // The difference between 1 and the next larger representable number.
2015 inline constexpr real128 epsilon_128 = real128_epsilon();
2016 
2017 // The smallest positive denormalized number.
2018 inline constexpr real128 denorm_min_128 = real128_denorm_min();
2019 
2020 // Inf.
2021 inline constexpr real128 inf_128 = real128_inf();
2022 
2023 // NaN.
2024 inline constexpr real128 nan_128 = real128_nan();
2025 
2026 // Pi.
2027 inline constexpr real128 pi_128 = real128_pi();
2028 
2029 // Euler's number.
2030 inline constexpr real128 e_128 = real128_e();
2031 
2032 // Quadruple-precision sqrt2.
2033 inline constexpr real128 sqrt2_128 = real128_sqrt2();
2034 
2035 #endif
2036 
2037 // Hash.
hash(const real128 & x)2038 inline std::size_t hash(const real128 &x)
2039 {
2040     // NOTE: in order to detect if x is zero/nan, resort to reading directly into the ieee fields.
2041     // This avoids calling the fpclassify() function, which internally invokes a compiler library function.
2042     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
2043     detail::ieee_float128 ief;
2044     ief.value = x.m_value;
2045     const auto is_zero = ief.i_eee.exponent == 0u && ief.i_eee.mant_low == 0u && ief.i_eee.mant_high == 0u;
2046     const auto is_nan = ief.i_eee.exponent == 32767ul && (ief.i_eee.mant_low != 0u || ief.i_eee.mant_high != 0u);
2047     // Read the bit-level representation of x and mix it up using a hash combiner.
2048     struct float128_split_t {
2049         // NOTE: unsigned long long is guaranteed to be at least 64-bit wide.
2050         unsigned long long part1 : 64;
2051         unsigned long long part2 : 64;
2052     };
2053     union float128_split {
2054         __float128 value;
2055         float128_split_t split;
2056     };
2057     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
2058     float128_split fs;
2059     fs.value = x.m_value;
2060     auto retval = fs.split.part1;
2061     // The hash combiner. This is lifted directly from Boost. See also:
2062     // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
2063     retval ^= fs.split.part2 + 0x9e3779b9ull + (retval << 6) + (retval >> 2);
2064     // This last step will set retval to zero if x is zero, and to -1 if x is NaN. We need this because:
2065     // - +0.0 and -0.0 have a different bit-level representation, but they are mathematically equal
2066     //   and they are equal according to operator==();
2067     // - we want to ensure that all NaN values produce the same hash.
2068     // NOLINTNEXTLINE(readability-implicit-bool-conversion)
2069     retval = (retval & static_cast<unsigned long long>(-!is_zero)) | static_cast<unsigned long long>(-is_nan);
2070     return static_cast<std::size_t>(retval);
2071 }
2072 
2073 // NOTE: put these definitions here, as we need the comparison operators to be available.
2074 #if defined(__INTEL_COMPILER)
2075 inline
2076 #else
2077 constexpr
2078 #endif
2079     bool
real128_equal_to(const real128 & x,const real128 & y)2080     real128_equal_to(const real128 &x, const real128 &y)
2081 {
2082     return (!x.isnan() && !y.isnan()) ? (x == y) : (x.isnan() && y.isnan());
2083 }
2084 
2085 #if defined(__INTEL_COMPILER)
2086 inline
2087 #else
2088 constexpr
2089 #endif
2090     bool
real128_lt(const real128 & x,const real128 & y)2091     real128_lt(const real128 &x, const real128 &y)
2092 {
2093     // NOTE: in case at least one op is NaN, we have the following possibilities:
2094     // - NaN vs NaN -> false,
2095     // - NaN vs not-NaN -> false,
2096     // - not-NaN vs NaN -> true.
2097     return (!x.isnan() && !y.isnan()) ? (x < y) : !x.isnan();
2098 }
2099 
2100 #if defined(__INTEL_COMPILER)
2101 inline
2102 #else
2103 constexpr
2104 #endif
2105     bool
real128_gt(const real128 & x,const real128 & y)2106     real128_gt(const real128 &x, const real128 &y)
2107 {
2108     // NOTE: in case at least one op is NaN, we have the following possibilities:
2109     // - NaN vs NaN -> false,
2110     // - NaN vs not-NaN -> true,
2111     // - not-NaN vs NaN -> false.
2112     return (!x.isnan() && !y.isnan()) ? (x > y) : !y.isnan();
2113 }
2114 
2115 // Implementation of integer's assignment
2116 // from real128.
2117 template <std::size_t SSize>
operator =(const real128 & x)2118 inline integer<SSize> &integer<SSize>::operator=(const real128 &x)
2119 {
2120     // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
2121     return *this = static_cast<integer<SSize>>(x);
2122 }
2123 
2124 // Implementation of rational's assignment
2125 // from real128.
2126 template <std::size_t SSize>
operator =(const real128 & x)2127 inline rational<SSize> &rational<SSize>::operator=(const real128 &x)
2128 {
2129     // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
2130     return *this = static_cast<rational<SSize>>(x);
2131 }
2132 
2133 } // namespace mppp
2134 
2135 #if defined(MPPP_WITH_BOOST_S11N)
2136 
2137 // Never track the address of real128 objects
2138 // during serialization.
2139 BOOST_CLASS_TRACKING(mppp::real128, boost::serialization::track_never)
2140 
2141 #endif
2142 
2143 namespace std
2144 {
2145 
2146 // Specialisation of std::numeric_limits for mppp::real128.
2147 template <>
2148 class numeric_limits<mppp::real128>
2149 {
2150 public:
2151     // NOTE: in C++17 and later, constexpr implies inline. That is,
2152     // we have no need for the initialisation of the static member outside the class.
2153     // Before C++17, however, we *should* do the external initialisation, but, since
2154     // this is a full specialisation, that would lead to multiply-defined references
2155     // when dealing with multiple translation units. So we avoid that, but that means
2156     // that certain uses of the static data members may lead to undefined references
2157     // before C++17. See:
2158     // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#454
2159     // NOTE: this is mostly lifted from Boost.Multiprecision.
2160     static constexpr bool is_specialized = true;
real128(min)2161     static constexpr mppp::real128(min)()
2162     {
2163         return mppp::real128_min();
2164     }
real128(max)2165     static constexpr mppp::real128(max)()
2166     {
2167         return mppp::real128_max();
2168     }
lowest()2169     static constexpr mppp::real128 lowest()
2170     {
2171         return -mppp::real128_max();
2172     }
2173     static constexpr int digits = 113;
2174     static constexpr int digits10 = 33;
2175     static constexpr int max_digits10 = 36;
2176     static constexpr bool is_signed = true;
2177     static constexpr bool is_integer = false;
2178     static constexpr bool is_exact = false;
2179     static constexpr int radix = 2;
epsilon()2180     static constexpr mppp::real128 epsilon()
2181     {
2182         return mppp::real128_epsilon();
2183     }
round_error()2184     static constexpr mppp::real128 round_error()
2185     {
2186         return mppp::real128{.5};
2187     }
2188     static constexpr int min_exponent = -16381;
2189     static constexpr int min_exponent10 = min_exponent * 301L / 1000L;
2190     static constexpr int max_exponent = 16384;
2191     static constexpr int max_exponent10 = max_exponent * 301L / 1000L;
2192     static constexpr bool has_infinity = true;
2193     static constexpr bool has_quiet_NaN = true;
2194     static constexpr bool has_signaling_NaN = false;
2195     static constexpr float_denorm_style has_denorm = denorm_present;
2196     static constexpr bool has_denorm_loss = true;
infinity()2197     static constexpr mppp::real128 infinity()
2198     {
2199         return mppp::real128_inf();
2200     }
quiet_NaN()2201     static constexpr mppp::real128 quiet_NaN()
2202     {
2203         return mppp::real128_nan();
2204     }
signaling_NaN()2205     static constexpr mppp::real128 signaling_NaN()
2206     {
2207         return mppp::real128{0};
2208     }
denorm_min()2209     static constexpr mppp::real128 denorm_min()
2210     {
2211         return mppp::real128_denorm_min();
2212     }
2213     static constexpr bool is_iec559 = true;
2214     static constexpr bool is_bounded = false;
2215     static constexpr bool is_modulo = false;
2216     static constexpr bool traps = false;
2217     static constexpr bool tinyness_before = false;
2218     static constexpr float_round_style round_style = round_to_nearest;
2219 };
2220 
2221 } // namespace std
2222 
2223 #include <mp++/detail/real128_literal.hpp>
2224 
2225 // Support for pretty printing in xeus-cling.
2226 #if defined(__CLING__)
2227 
2228 #if __has_include(<nlohmann/json.hpp>)
2229 
2230 #include <nlohmann/json.hpp>
2231 
2232 namespace mppp
2233 {
2234 
mime_bundle_repr(const real128 & x)2235 inline nlohmann::json mime_bundle_repr(const real128 &x)
2236 {
2237     auto bundle = nlohmann::json::object();
2238 
2239     bundle["text/plain"] = x.to_string();
2240 
2241     return bundle;
2242 }
2243 
2244 } // namespace mppp
2245 
2246 #endif
2247 
2248 #endif
2249 
2250 #else
2251 
2252 #error The real128.hpp header was included but mp++ was not configured with the MPPP_WITH_QUADMATH option.
2253 
2254 #endif
2255 
2256 #endif
2257