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