Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
mpreal.hpp
Go to the documentation of this file.
1#pragma once
2
5#include "exact_rational.hpp"
6#include "math_constants.hpp"
7#include "scalar_traits.hpp"
8#include <memory>
9#include <mpfr.h>
10#include <ostream>
12#include <variant>
13
14namespace uni20
15{
16namespace detail
17{
18struct mpreal_access;
19struct mpcomplex_access;
20// Provider limits belong here, not in the generic precision description.
21inline mpfr_prec_t mpfr_precision(Precision p)
22{
23 auto bits = p.bit_count();
24 if (bits < MPFR_PREC_MIN || bits > MPFR_PREC_MAX) throw std::invalid_argument("MPFR: unsupported significand size");
25 return static_cast<mpfr_prec_t>(bits);
26}
27inline void require_mpfr_tls()
28{
29 static bool const supported = mpfr_buildopt_tls_p() != 0;
30 if (!supported) throw std::runtime_error("uni20::mpreal requires a thread-safe MPFR build");
31}
32
33// Only the active variant alternative owns a numerical payload. MPFR retains
34// finite precision; the variant discriminator records exact/approximate/unset.
35struct mpfr_value
36{
37 mpfr_t value{};
38 bool initialized = false;
39 explicit mpfr_value(Precision p)
40 {
41 require_mpfr_tls();
42 mpfr_init2(value, mpfr_precision(p));
43 initialized = true;
44 mpfr_set_zero(value, 1);
45 }
46 mpfr_value(mpfr_value const& other) : mpfr_value(Precision::bits(mpfr_get_prec(other.value)))
47 {
48 mpfr_set(value, other.value, MPFR_RNDN);
49 }
50 mpfr_value(mpfr_value&& other) noexcept
51 {
52 std::swap(value[0], other.value[0]);
53 std::swap(initialized, other.initialized);
54 }
55 mpfr_value& operator=(mpfr_value other) noexcept
56 {
57 std::swap(value[0], other.value[0]);
58 std::swap(initialized, other.initialized);
59 return *this;
60 }
61 ~mpfr_value()
62 {
63 if (initialized) mpfr_clear(value);
64 }
65};
66} // namespace detail
67
74class mpreal {
75 public:
77 mpreal() : value_(std::in_place_type<exact_rational>) {}
79 explicit mpreal(uninitialized_t) noexcept : value_(std::monostate{}) {}
82 {
83 if (p.is_exact())
84 value_.emplace<exact_rational>();
85 else
86 value_.emplace<detail::mpfr_value>(p);
87 }
91 {
92 (void)value.native_handle();
93 value_.emplace<exact_rational>(std::move(value));
94 }
96 template <std::integral I>
97 requires(!std::same_as<I, bool>)
99 {}
100
102 mpreal(std::string_view text, Precision p) : mpreal(p)
103 {
104 if (p.is_exact() || text.find('/') != std::string_view::npos)
105 {
106 *this = mpreal(exact_rational(text), p);
107 return;
108 }
109 if (text.empty() || text.find('\0') != std::string_view::npos ||
110 text.find_first_of(" \t\r\n\f\v") != std::string_view::npos)
111 throw std::invalid_argument("mpreal: expected a complete decimal value");
112 std::string owned(text);
113 char* end = nullptr;
114 mpfr_strtofr(this->approximate(), owned.c_str(), &end, 10, MPFR_RNDN);
115 if (end != owned.c_str() + owned.size() || end == owned.c_str())
116 throw std::invalid_argument("mpreal: invalid decimal value");
117 }
119 {
120 auto rational = value.native_handle();
121 if (p.is_exact())
122 value_ = value;
123 else
124 mpfr_set_q(this->approximate(), rational, MPFR_RNDN);
125 }
127 template <std::integral I>
128 requires(!std::same_as<I, bool>)
131
134 explicit mpreal(mpfr_srcptr value, Precision p) : mpreal(p) { mpfr_set(this->approximate(), value, MPFR_RNDN); }
136 template <class F>
137 requires(std::same_as<F, float> || std::same_as<F, double> || std::same_as<F, long double>)
139 {
140 mpfr_set_ld(this->approximate(), static_cast<long double>(value), MPFR_RNDN);
141 }
142#if UNI20_HAS_FLOAT128
144 template <class F>
145 requires std::same_as<F, float128>
147 {
148 mpfr_set_float128(this->approximate(), value, MPFR_RNDN);
149 }
150#endif
153 mpreal(mpreal const& other, Precision p) : mpreal(p)
154 {
155 other.require_value();
156 if (p.is_exact())
157 {
158 if (!other.is_exact()) throw std::invalid_argument("mpreal: an approximation cannot be reclassified as exact");
159 value_ = other.exact_value();
160 }
161 else
162 other.copy_to(this->approximate());
163 }
164 mpreal(mpreal const&) = default;
166 mpreal(mpreal&& other) noexcept : value_(std::move(other.value_)) { other.value_.emplace<std::monostate>(); }
167 mpreal& operator=(mpreal other) noexcept
168 {
169 this->swap(other);
170 return *this;
171 }
172 void swap(mpreal& other) noexcept { value_.swap(other.value_); }
174 bool initialized() const noexcept { return !std::holds_alternative<std::monostate>(value_); }
176 bool is_exact() const noexcept { return std::holds_alternative<exact_rational>(value_); }
179 {
180 this->require_value();
181 return this->is_exact() ? Precision::exact() : Precision::bits(mpfr_get_prec(this->native_handle()));
182 }
184 mpfr_srcptr native_handle() const
185 {
186 this->require_value();
187 if (this->is_exact()) throw std::logic_error("mpreal: exact value needs finite precision before MPFR access");
188 return std::get<detail::mpfr_value>(value_).value;
189 }
192 {
193 this->require_value();
194 if (!this->is_exact()) throw std::logic_error("mpreal: value is approximate");
195 return std::get<exact_rational>(value_);
196 }
198 void copy_to(mpfr_ptr destination) const
199 {
200 this->require_value();
201 if (this->is_exact())
202 mpfr_set_q(destination, this->exact_value().native_handle(), MPFR_RNDN);
203 else
204 mpfr_set(destination, this->native_handle(), MPFR_RNDN);
205 }
206 mpreal at(Precision p) const { return mpreal(*this, p); }
208 explicit operator float() const { return this->to_native<float, mpfr_get_flt>(); }
209 explicit operator double() const { return this->to_native<double, mpfr_get_d>(); }
210 explicit operator long double() const { return this->to_native<long double, mpfr_get_ld>(); }
211#if UNI20_HAS_FLOAT128
213 explicit operator float128() const { return this->to_native<float128, mpfr_get_float128>(113); }
214#endif
215
219 std::string to_string(std::size_t digits = 0) const
220 {
221 if (this->is_exact()) return this->exact_value().to_string();
222 auto value = this->native_handle();
223 if (mpfr_nan_p(value)) return "nan";
224 if (mpfr_inf_p(value)) return mpfr_signbit(value) ? "-inf" : "inf";
225 if (mpfr_zero_p(value)) return mpfr_signbit(value) ? "-0" : "0";
226 mpfr_exp_t exponent;
227 std::unique_ptr<char, decltype(&mpfr_free_str)> text(
228 mpfr_get_str(nullptr, &exponent, 10, digits, value, MPFR_RNDN), &mpfr_free_str);
229 if (!text) throw std::bad_alloc();
230 std::string_view mantissa(text.get());
231 std::string result;
232 if (mantissa.front() == '-')
233 {
234 result += '-';
235 mantissa.remove_prefix(1);
236 }
237 result += mantissa.front();
238 if (mantissa.size() > 1)
239 {
240 result += '.';
241 result += mantissa.substr(1);
242 }
243 result += 'e';
244 result += std::to_string(exponent - 1);
245 return result;
246 }
247
248 mpreal& operator+=(mpreal const& rhs);
249 mpreal& operator-=(mpreal const& rhs);
250 mpreal& operator*=(mpreal const& rhs);
251 mpreal& operator/=(mpreal const& rhs);
252 mpreal& operator+=(exact_rational const& rhs);
253 mpreal& operator-=(exact_rational const& rhs);
254 mpreal& operator*=(exact_rational const& rhs);
255 mpreal& operator/=(exact_rational const& rhs);
256
257 private:
259 friend struct detail::mpreal_access;
260 friend struct detail::mpcomplex_access;
261 template <class F, auto Convert> F to_native(int digits = uni20::numeric_limits<F>::digits()) const
262 {
263 this->require_value();
264 if (!this->is_exact()) return Convert(this->native_handle(), MPFR_RNDN);
265 // Enclose the rational and refine until both bounds round identically.
266 // This avoids double rounding, including at native subnormal boundaries.
267 auto bits = static_cast<mpfr_prec_t>(digits + 16);
268 for (;;)
269 {
270 detail::mpfr_value lower(Precision::bits(bits)), upper(Precision::bits(bits));
271 mpfr_set_q(lower.value, this->exact_value().native_handle(), MPFR_RNDD);
272 mpfr_set_q(upper.value, this->exact_value().native_handle(), MPFR_RNDU);
273 F lo = Convert(lower.value, MPFR_RNDN), hi = Convert(upper.value, MPFR_RNDN);
274 if (lo == hi) return lo;
275 if (bits > MPFR_PREC_MAX / 2) throw std::overflow_error("mpreal: native conversion precision exhausted");
276 bits *= 2;
277 }
278 }
279 void require_value() const
280 {
281 if (!this->initialized()) throw std::logic_error("mpreal: numerical use of an unset value");
282 }
283 mpfr_ptr approximate()
284 {
285 (void)this->native_handle();
286 return std::get<detail::mpfr_value>(value_).value;
287 }
288 std::variant<std::monostate, exact_rational, detail::mpfr_value> value_;
289};
290
296template <> struct numeric_limits<mpreal>
297{
298 static constexpr bool is_specialized = true;
299 static constexpr bool is_signed = true;
300 static constexpr bool is_integer = false;
301 static constexpr bool is_exact = false;
302 static constexpr bool is_bounded = false;
303 static constexpr bool is_iec559 = false;
304 static constexpr int radix = 2;
305 static constexpr bool has_infinity = true;
306 static constexpr bool has_quiet_NaN = true;
307
309 static mpfr_prec_t digits(Precision p) { return p.bit_count(); }
310 static mpfr_prec_t digits(mpreal const& x) { return digits(x.precision()); }
311
314 static mpreal epsilon(Precision p);
315 static mpreal epsilon(mpreal const& x) { return epsilon(x.precision()); }
316 static mpreal epsilon() = delete;
317};
318
319namespace detail
320{
321struct mpreal_access
322{
323 // Construct results without exposing mutable provider handles on the scalar.
324 template <class F> static mpreal finite_result(Precision p, F&& operation)
325 {
326 (void)p.bit_count();
327 mpreal result(p);
328 operation(result.approximate());
329 return result;
330 }
331 template <class F> static exact_rational rational_result(F&& operation)
332 {
333 exact_rational result;
334 operation(result.value_);
335 mpq_canonicalize(result.value_);
336 return result;
337 }
338
339 static bool can_expand_rational(mpreal const& x)
340 {
341 if (x.is_exact() || mpfr_zero_p(x.native_handle())) return true;
342 auto bound = std::max<mpfr_prec_t>(4096, mpfr_get_prec(x.native_handle()));
343 auto exponent = mpfr_get_exp(x.native_handle());
344 return exponent >= -bound && exponent <= bound;
345 }
346 static scaled_rational stored_scaled(mpreal const& x)
347 {
348 if (x.is_exact()) return {x.exact_value(), 0};
349 scaled_rational result;
350 if (!mpfr_zero_p(x.native_handle()))
351 result.exponent = mpfr_get_z_2exp(mpq_numref(result.coefficient.value_), x.native_handle());
352 return result;
353 }
354 // Internal arithmetic representation of a finite stored value. This does
355 // not reclassify the user's approximation as an exact scalar.
356 static exact_rational stored_rational(mpreal const& x)
357 {
358 if (x.is_exact()) return x.exact_value();
359 exact_rational result;
360 mpfr_get_q(result.value_, x.native_handle());
361 return result;
362 }
363
364 template <auto Operation> static mpreal mixed(mpreal const& a, mpreal const& b, Precision p)
365 {
366 auto const& real = a.is_exact() ? b : a;
367 auto const& rational = a.is_exact() ? a.exact_value() : b.exact_value();
368 auto q = rational.native_handle();
369 mpreal result(p);
370 if (rational == 0)
371 {
372 // MPFR's _q helpers may copy their floating operand for q == 0.
373 // Use floating +0 to preserve binary-operation signed-zero semantics.
374 auto r = real.native_handle();
375 auto zero = result.native_handle();
376 Operation(result.approximate(), a.is_exact() ? zero : r, a.is_exact() ? r : zero, MPFR_RNDN);
377 return result;
378 }
379 if constexpr (same_function_v<Operation, mpfr_add>)
380 mpfr_add_q(result.approximate(), real.native_handle(), q, MPFR_RNDN);
381 else if constexpr (same_function_v<Operation, mpfr_mul>)
382 mpfr_mul_q(result.approximate(), real.native_handle(), q, MPFR_RNDN);
383 else if constexpr (same_function_v<Operation, mpfr_sub>)
384 {
385 if (a.is_exact())
386 {
387 // Negation is exact, including signed zero; adding q then rounds once.
388 mpfr_neg(result.approximate(), real.native_handle(), MPFR_RNDN);
389 mpfr_add_q(result.approximate(), result.native_handle(), q, MPFR_RNDN);
390 }
391 else
392 mpfr_sub_q(result.approximate(), real.native_handle(), q, MPFR_RNDN);
393 }
394 else if constexpr (same_function_v<Operation, mpfr_div>)
395 {
396 if (!a.is_exact())
397 mpfr_div_q(result.approximate(), real.native_handle(), q, MPFR_RNDN);
398 else if (mpfr_number_p(real.native_handle()) && !mpfr_zero_p(real.native_handle()) && rational != 0)
399 {
400 if (can_expand_rational(real))
401 result = mpreal(rational / stored_rational(real), p);
402 else
403 scaled_rational_ratio({stored_scaled(a), {}, stored_scaled(b), {}}).round_to(result.approximate());
404 }
405 else
406 {
407 // Only signs and zero matter here. Do not overflow/underflow q while
408 // preparing zero, infinity or NaN arithmetic.
409 mpfr_set_si(result.approximate(), mpq_sgn(q), MPFR_RNDN);
410 mpfr_div(result.approximate(), result.native_handle(), real.native_handle(), MPFR_RNDN);
411 }
412 }
413 return result;
414 }
415 // For rational x and base 2 or 10, a rational logarithm is an integer.
416 // Check numerator and denominator independently without any floating conversion.
417 static mpreal exact_logarithm(exact_rational const& x, unsigned long base)
418 {
419 auto q = x.native_handle();
420 if (mpq_sgn(q) > 0)
421 {
422 mp_integer factor, remainder;
423 mpz_set_ui(factor.value, base);
424 auto numerator_power = mpz_remove(remainder.value, mpq_numref(q), factor.value);
425 if (mpz_cmp_ui(remainder.value, 1) == 0)
426 {
427 auto denominator_power = mpz_remove(remainder.value, mpq_denref(q), factor.value);
428 if (mpz_cmp_ui(remainder.value, 1) == 0)
429 return mpreal(exact_rational(numerator_power) - exact_rational(denominator_power));
430 }
431 }
432 throw std::logic_error("mpreal: logarithm requires finite working precision");
433 }
434
435 // Explicit operation precision means convert the inputs, then evaluate.
436 template <auto Operation> static mpreal apply(mpreal const& x, Precision p)
437 {
438 (void)p.bit_count();
439 return apply<Operation>(x.at(p));
440 }
441 template <auto Operation> static mpreal apply(mpreal const& x, mpreal const& y, Precision p)
442 {
443 (void)p.bit_count();
444 return apply<Operation>(x.at(p), y.at(p));
445 }
446
447 template <auto Operation> static mpreal apply(mpreal const& x)
448 {
449 auto p = x.precision();
450 if (p.is_exact())
451 {
452 if constexpr (same_function_v<Operation, mpfr_neg>)
453 return mpreal(-x.exact_value());
454 else if constexpr (same_function_v<Operation, mpfr_abs>)
455 return mpreal(x.exact_value() < 0 ? -x.exact_value() : x.exact_value());
456 else if constexpr (same_function_v<Operation, mpfr_sqrt>)
457 return mpreal(uni20::sqrt(x.exact_value()));
458 else if constexpr (same_function_v<Operation, mpfr_cbrt>)
459 {
460 if (auto root = rational_root(x.exact_value(), 3)) return mpreal(*root);
461 throw std::logic_error("mpreal: cube root requires finite working precision");
462 }
463 else if constexpr (same_function_v<Operation, mpfr_exp2>)
465 else if constexpr (same_function_v<Operation, mpfr_log2>)
466 return exact_logarithm(x.exact_value(), 2);
467 else if constexpr (same_function_v<Operation, mpfr_log10>)
468 return exact_logarithm(x.exact_value(), 10);
469 else
470 {
471 auto const& q = x.exact_value();
472 if constexpr (same_function_v<Operation, mpfr_exp> || same_function_v<Operation, mpfr_cos> ||
473 same_function_v<Operation, mpfr_cosh>)
474 {
475 if (q == 0) return mpreal{1};
476 }
477 else if constexpr (same_function_v<Operation, mpfr_log> || same_function_v<Operation, mpfr_acos> ||
478 same_function_v<Operation, mpfr_acosh>)
479 {
480 if (q == 1) return mpreal{};
481 }
482 else if constexpr (same_function_v<Operation, mpfr_sin> || same_function_v<Operation, mpfr_tan> ||
483 same_function_v<Operation, mpfr_atan> || same_function_v<Operation, mpfr_expm1> ||
484 same_function_v<Operation, mpfr_log1p> || same_function_v<Operation, mpfr_asin> ||
485 same_function_v<Operation, mpfr_sinh> || same_function_v<Operation, mpfr_tanh> ||
486 same_function_v<Operation, mpfr_asinh> || same_function_v<Operation, mpfr_atanh>)
487 {
488 if (q == 0) return mpreal{};
489 }
490 throw std::logic_error("mpreal: elementary function requires finite working precision");
491 }
492 }
493 mpreal result(p);
494 Operation(result.approximate(), x.native_handle(), MPFR_RNDN);
495 return result;
496 }
497 template <auto Operation> static mpreal apply(mpreal const& a, mpreal const& b)
498 {
499 auto p = common_precision(a.precision(), b.precision());
500 if (p.is_exact())
501 {
502 if constexpr (same_function_v<Operation, mpfr_add>)
503 return mpreal(a.exact_value() + b.exact_value());
504 else if constexpr (same_function_v<Operation, mpfr_sub>)
505 return mpreal(a.exact_value() - b.exact_value());
506 else if constexpr (same_function_v<Operation, mpfr_mul>)
507 return mpreal(a.exact_value() * b.exact_value());
508 else if constexpr (same_function_v<Operation, mpfr_div>)
509 return mpreal(a.exact_value() / b.exact_value());
510 else if constexpr (same_function_v<Operation, mpfr_pow>)
511 return mpreal(uni20::pow(a.exact_value(), b.exact_value()));
512 else if constexpr (same_function_v<Operation, mpfr_hypot>)
513 return mpreal(uni20::sqrt(a.exact_value() * a.exact_value() + b.exact_value() * b.exact_value()));
514 else
515 {
516 if constexpr (same_function_v<Operation, mpfr_atan2>)
517 if (a.exact_value() == 0 && b.exact_value() > 0) return mpreal{};
518 throw std::logic_error("mpreal: elementary function requires finite working precision");
519 }
520 }
521 if constexpr (same_function_v<Operation, mpfr_add> || same_function_v<Operation, mpfr_sub> ||
522 same_function_v<Operation, mpfr_mul> || same_function_v<Operation, mpfr_div>)
523 if (a.is_exact() != b.is_exact()) return mixed<Operation>(a, b, p);
525 if (a.is_exact()) av = a.at(p);
526 if (b.is_exact()) bv = b.at(p);
527 mpreal result(p);
528 Operation(result.approximate(), a.is_exact() ? av.native_handle() : a.native_handle(),
529 b.is_exact() ? bv.native_handle() : b.native_handle(), MPFR_RNDN);
530 return result;
531 }
532 template <auto Operation> static mpreal& assign(mpreal& a, mpreal const& b)
533 {
534 if (!a.is_exact() && !b.is_exact())
535 {
536 (void)common_precision(a.precision(), b.precision());
537 Operation(a.approximate(), a.native_handle(), b.native_handle(), MPFR_RNDN);
538 }
539 else
540 a = apply<Operation>(a, b);
541 return a;
542 }
543 template <auto Operation> static mpreal constant(Precision p)
544 {
545 (void)p.bit_count();
546 mpreal result(p);
547 Operation(result.approximate(), MPFR_RNDN);
548 return result;
549 }
550};
551} // namespace detail
552
553inline mpreal exact_rational::at(Precision precision) const { return mpreal(*this, precision); }
554inline mpreal decimal_literal::at(Precision precision) const { return mpreal(*this, precision); }
555
556inline mpreal operator+(mpreal const& a, mpreal const& b) { return detail::mpreal_access::apply<mpfr_add>(a, b); }
557inline mpreal operator-(mpreal const& a, mpreal const& b) { return detail::mpreal_access::apply<mpfr_sub>(a, b); }
558inline mpreal operator*(mpreal const& a, mpreal const& b) { return detail::mpreal_access::apply<mpfr_mul>(a, b); }
559inline mpreal operator/(mpreal const& a, mpreal const& b) { return detail::mpreal_access::apply<mpfr_div>(a, b); }
560inline mpreal operator-(mpreal const& a) { return detail::mpreal_access::apply<mpfr_neg>(a); }
561inline mpreal operator+(mpreal const& a)
562{
563 (void)a.precision();
564 return a;
565}
566
567// Basic arithmetic retains the exact operand until rounding the result.
568inline mpreal operator+(mpreal const& a, exact_rational const& b) { return a + mpreal(b); }
569inline mpreal operator-(mpreal const& a, exact_rational const& b) { return a - mpreal(b); }
570inline mpreal operator*(mpreal const& a, exact_rational const& b) { return a * mpreal(b); }
571inline mpreal operator/(mpreal const& a, exact_rational const& b) { return a / mpreal(b); }
572inline mpreal operator+(exact_rational const& a, mpreal const& b) { return mpreal(a) + b; }
573inline mpreal operator-(exact_rational const& a, mpreal const& b) { return mpreal(a) - b; }
574inline mpreal operator*(exact_rational const& a, mpreal const& b) { return mpreal(a) * b; }
575inline mpreal operator/(exact_rational const& a, mpreal const& b) { return mpreal(a) / b; }
576
577inline mpreal& mpreal::operator+=(mpreal const& rhs) { return detail::mpreal_access::assign<mpfr_add>(*this, rhs); }
578inline mpreal& mpreal::operator-=(mpreal const& rhs) { return detail::mpreal_access::assign<mpfr_sub>(*this, rhs); }
579inline mpreal& mpreal::operator*=(mpreal const& rhs) { return detail::mpreal_access::assign<mpfr_mul>(*this, rhs); }
580inline mpreal& mpreal::operator/=(mpreal const& rhs) { return detail::mpreal_access::assign<mpfr_div>(*this, rhs); }
581inline mpreal& mpreal::operator+=(exact_rational const& rhs) { return *this += mpreal(rhs); }
582inline mpreal& mpreal::operator-=(exact_rational const& rhs) { return *this -= mpreal(rhs); }
583inline mpreal& mpreal::operator*=(exact_rational const& rhs) { return *this *= mpreal(rhs); }
584inline mpreal& mpreal::operator/=(exact_rational const& rhs) { return *this /= mpreal(rhs); }
585
587inline std::partial_ordering operator<=>(mpreal const& a, mpreal const& b)
588{
589 (void)a.precision();
590 (void)b.precision();
591 if (a.is_exact() && b.is_exact()) return a.exact_value() <=> b.exact_value();
592 if (a.is_exact())
593 {
594 if (mpfr_nan_p(b.native_handle())) return std::partial_ordering::unordered;
595 return 0 <=> mpfr_cmp_q(b.native_handle(), a.exact_value().native_handle());
596 }
597 if (b.is_exact())
598 {
599 if (mpfr_nan_p(a.native_handle())) return std::partial_ordering::unordered;
600 return mpfr_cmp_q(a.native_handle(), b.exact_value().native_handle()) <=> 0;
601 }
602 if (mpfr_unordered_p(a.native_handle(), b.native_handle())) return std::partial_ordering::unordered;
603 return mpfr_cmp(a.native_handle(), b.native_handle()) <=> 0;
604}
605inline bool operator==(mpreal const& a, mpreal const& b) { return (a <=> b) == 0; }
606inline std::partial_ordering operator<=>(mpreal const& a, exact_rational const& b) { return a <=> mpreal(b); }
607inline bool operator==(mpreal const& a, exact_rational const& b) { return (a <=> b) == 0; }
608
609namespace detail
610{
611template <std::integral I> int compare_integer(mpreal const& a, I b)
612{
613 if constexpr (std::is_signed_v<I> && std::numeric_limits<I>::digits <= std::numeric_limits<long>::digits)
614 return mpfr_cmp_si(a.native_handle(), static_cast<long>(b));
615 else if constexpr (!std::is_signed_v<I> &&
616 std::numeric_limits<I>::digits <= std::numeric_limits<unsigned long>::digits)
617 return mpfr_cmp_ui(a.native_handle(), static_cast<unsigned long>(b));
618 else
619 return mpfr_cmp_q(a.native_handle(), exact_rational(b).native_handle());
620}
621} // namespace detail
622
623template <std::integral I>
624 requires(!std::same_as<I, bool>)
625inline bool operator==(mpreal const& a, I b)
626{
627 if (a.is_exact()) return a.exact_value() == exact_rational(b);
628 if (b == 0) return mpfr_zero_p(a.native_handle());
629 return mpfr_number_p(a.native_handle()) && detail::compare_integer(a, b) == 0;
630}
631template <std::integral I>
632 requires(!std::same_as<I, bool>)
633inline std::partial_ordering operator<=>(mpreal const& a, I b)
634{
635 if (a.is_exact()) return a.exact_value() <=> exact_rational(b);
636 if (mpfr_nan_p(a.native_handle())) return std::partial_ordering::unordered;
637 return detail::compare_integer(a, b) <=> 0;
638}
639inline bool isfinite(mpreal const& x) { return x.is_exact() || mpfr_number_p(x.native_handle()); }
640inline bool isnan(mpreal const& x) { return !x.is_exact() && mpfr_nan_p(x.native_handle()); }
641inline bool isinf(mpreal const& x) { return !x.is_exact() && mpfr_inf_p(x.native_handle()); }
642inline bool signbit(mpreal const& x)
643{
644 return x.is_exact() ? mpq_sgn(x.exact_value().native_handle()) < 0 : mpfr_signbit(x.native_handle());
645}
646inline mpreal conj(mpreal const& x)
647{
648 (void)x.precision();
649 return x;
650}
651inline mpreal abs(mpreal const& x) { return detail::mpreal_access::apply<mpfr_abs>(x); }
652inline mpreal sqrt(mpreal const& x) { return detail::mpreal_access::apply<mpfr_sqrt>(x); }
653inline mpreal exp(mpreal const& x) { return detail::mpreal_access::apply<mpfr_exp>(x); }
654inline mpreal log(mpreal const& x) { return detail::mpreal_access::apply<mpfr_log>(x); }
655inline mpreal sin(mpreal const& x) { return detail::mpreal_access::apply<mpfr_sin>(x); }
656inline mpreal cos(mpreal const& x) { return detail::mpreal_access::apply<mpfr_cos>(x); }
657inline mpreal tan(mpreal const& x) { return detail::mpreal_access::apply<mpfr_tan>(x); }
658inline mpreal atan(mpreal const& x) { return detail::mpreal_access::apply<mpfr_atan>(x); }
659inline mpreal atan2(mpreal const& y, mpreal const& x) { return detail::mpreal_access::apply<mpfr_atan2>(y, x); }
660inline mpreal hypot(mpreal const& x, mpreal const& y) { return detail::mpreal_access::apply<mpfr_hypot>(x, y); }
661inline mpreal pow(mpreal const& x, mpreal const& y) { return detail::mpreal_access::apply<mpfr_pow>(x, y); }
662
665inline mpreal abs(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_abs>(x, p); }
668inline mpreal sqrt(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_sqrt>(x, p); }
671inline mpreal exp(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_exp>(x, p); }
674inline mpreal log(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_log>(x, p); }
677inline mpreal sin(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_sin>(x, p); }
680inline mpreal cos(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_cos>(x, p); }
683inline mpreal tan(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_tan>(x, p); }
686inline mpreal atan(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_atan>(x, p); }
689inline mpreal atan2(mpreal const& y, mpreal const& x, Precision p)
690{
691 return detail::mpreal_access::apply<mpfr_atan2>(y, x, p);
692}
695inline mpreal hypot(mpreal const& x, mpreal const& y, Precision p)
696{
697 return detail::mpreal_access::apply<mpfr_hypot>(x, y, p);
698}
701inline mpreal pow(mpreal const& x, mpreal const& y, Precision p)
702{
703 return detail::mpreal_access::apply<mpfr_pow>(x, y, p);
704}
705
708inline mpreal cbrt(mpreal const& x) { return detail::mpreal_access::apply<mpfr_cbrt>(x); }
711inline mpreal cbrt(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_cbrt>(x, p); }
712
715inline mpreal exp2(mpreal const& x) { return detail::mpreal_access::apply<mpfr_exp2>(x); }
718inline mpreal exp2(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_exp2>(x, p); }
719
722inline mpreal expm1(mpreal const& x) { return detail::mpreal_access::apply<mpfr_expm1>(x); }
725inline mpreal expm1(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_expm1>(x, p); }
726
729inline mpreal log2(mpreal const& x) { return detail::mpreal_access::apply<mpfr_log2>(x); }
732inline mpreal log2(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_log2>(x, p); }
733
736inline mpreal log10(mpreal const& x) { return detail::mpreal_access::apply<mpfr_log10>(x); }
739inline mpreal log10(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_log10>(x, p); }
740
743inline mpreal log1p(mpreal const& x) { return detail::mpreal_access::apply<mpfr_log1p>(x); }
746inline mpreal log1p(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_log1p>(x, p); }
747
750inline mpreal asin(mpreal const& x) { return detail::mpreal_access::apply<mpfr_asin>(x); }
753inline mpreal asin(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_asin>(x, p); }
754
757inline mpreal acos(mpreal const& x) { return detail::mpreal_access::apply<mpfr_acos>(x); }
760inline mpreal acos(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_acos>(x, p); }
761
764inline mpreal sinh(mpreal const& x) { return detail::mpreal_access::apply<mpfr_sinh>(x); }
767inline mpreal sinh(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_sinh>(x, p); }
768
771inline mpreal cosh(mpreal const& x) { return detail::mpreal_access::apply<mpfr_cosh>(x); }
774inline mpreal cosh(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_cosh>(x, p); }
775
778inline mpreal tanh(mpreal const& x) { return detail::mpreal_access::apply<mpfr_tanh>(x); }
781inline mpreal tanh(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_tanh>(x, p); }
782
785inline mpreal asinh(mpreal const& x) { return detail::mpreal_access::apply<mpfr_asinh>(x); }
788inline mpreal asinh(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_asinh>(x, p); }
789
792inline mpreal acosh(mpreal const& x) { return detail::mpreal_access::apply<mpfr_acosh>(x); }
795inline mpreal acosh(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_acosh>(x, p); }
796
799inline mpreal atanh(mpreal const& x) { return detail::mpreal_access::apply<mpfr_atanh>(x); }
802inline mpreal atanh(mpreal const& x, Precision p) { return detail::mpreal_access::apply<mpfr_atanh>(x, p); }
803
805inline mpreal epsilon(Precision precision)
806{
807 // MPFR can construct this exact power of two without ambient precision changes.
808 (void)precision.bit_count();
809 mpreal result(precision);
810 mpfr_set_ui_2exp(result.approximate(), 1, 1 - precision.bit_count(), MPFR_RNDN);
811 return result;
812}
813
815
816namespace detail
817{
818template <auto Provider> struct mp_constant
819{
820 mpreal at(Precision p) const { return mpreal_access::constant<Provider>(p); }
821 friend mpreal operator+(mpreal const& x, mp_constant c) { return x + c.at(x.precision()); }
822 friend mpreal operator-(mpreal const& x, mp_constant c) { return x - c.at(x.precision()); }
823 friend mpreal operator*(mpreal const& x, mp_constant c) { return x * c.at(x.precision()); }
824 friend mpreal operator/(mpreal const& x, mp_constant c) { return x / c.at(x.precision()); }
825 friend mpreal operator+(mp_constant c, mpreal const& x) { return c.at(x.precision()) + x; }
826 friend mpreal operator-(mp_constant c, mpreal const& x) { return c.at(x.precision()) - x; }
827 friend mpreal operator*(mp_constant c, mpreal const& x) { return c.at(x.precision()) * x; }
828 friend mpreal operator/(mp_constant c, mpreal const& x) { return c.at(x.precision()) / x; }
829};
830template <> struct math_constants<mpreal>
831{
832 static constexpr mp_constant<mpfr_const_pi> pi{};
833 static constexpr mp_constant<mpfr_const_log2> log_two{};
834 static constexpr mp_constant<mpfr_const_euler> euler_gamma{};
835 static constexpr mp_constant<mpfr_const_catalan> catalan{};
836};
837} // namespace detail
838
840inline std::ostream& operator<<(std::ostream& os, mpreal const& value) { return os << value.to_string(); }
841inline std::ostream& operator<<(std::ostream& os, exact_rational const& value) { return os << value.to_string(); }
842
843} // namespace uni20
844
845#include "mpreal_functions.hpp"
846
847#include "mpreal_special.hpp"
848
849#include "mpreal_extensions.hpp"
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
static constexpr Precision bits(bit_count_type count)
Select a positive binary significand size.
Definition core/precision.hpp:21
constexpr bit_count_type bit_count() const
Number of binary significand bits.
Definition core/precision.hpp:43
static constexpr Precision exact() noexcept
No approximation has been selected; rational arithmetic remains exact.
Definition core/precision.hpp:17
A precisionless decimal spelling produced by the _mp literal operator.
Definition exact_rational.hpp:27
mpreal at(Precision precision) const
Materialize the literal with one rounding to the requested precision.
Definition mpreal.hpp:554
Owning arbitrary-size rational with exact, eager arithmetic.
Definition exact_rational.hpp:161
std::string to_string() const
Return the canonical numerator/denominator, or an integer when exact.
Definition exact_rational.hpp:225
mpreal at(Precision precision) const
Convert to a real value with one rounding at explicit precision.
Definition mpreal.hpp:553
mpq_srcptr native_handle() const
Borrow the read-only GMP rational while this object retains its payload.
Definition exact_rational.hpp:218
Owning exact rational or finite-precision MPFR real, with eager arithmetic.
Definition mpreal.hpp:74
mpreal(Precision p)
Construct zero at finite precision, or exact zero.
Definition mpreal.hpp:81
mpreal(F value, Precision p)
Import float, double or long double; precision must be finite.
Definition mpreal.hpp:138
mpreal at(Precision p) const
Definition mpreal.hpp:206
mpreal()
Construct exact zero for ordinary generic accumulators.
Definition mpreal.hpp:77
mpreal(exact_rational const &value, Precision p)
Definition mpreal.hpp:118
mpreal(decimal_literal value, Precision p)
Definition mpreal.hpp:126
mpreal & operator-=(mpreal const &rhs)
Definition mpreal.hpp:578
mpreal(I value, Precision p)
Definition mpreal.hpp:129
mpreal(mpreal const &)=default
mpreal(mpreal &&other) noexcept
Transfer ownership, leaving the source unset.
Definition mpreal.hpp:166
mpfr_srcptr native_handle() const
Borrow an approximate MPFR value; exact and unset states require explicit materialization first.
Definition mpreal.hpp:184
mpreal(exact_rational value)
Retain an exact rational without rounding.
Definition mpreal.hpp:90
std::string to_string(std::size_t digits=0) const
Format exact rationals as numerator/denominator and approximations as decimal scientific notation.
Definition mpreal.hpp:219
mpreal & operator+=(mpreal const &rhs)
Definition mpreal.hpp:577
mpreal(std::string_view text, Precision p)
Parse a complete decimal or integer fraction at the requested precision.
Definition mpreal.hpp:102
mpreal & operator=(mpreal other) noexcept
Definition mpreal.hpp:167
bool is_exact() const noexcept
Whether the active payload is an exact rational.
Definition mpreal.hpp:176
mpreal(mpfr_srcptr value, Precision p)
Copy a valid borrowed MPFR value, rounding at finite precision.
Definition mpreal.hpp:134
mpreal(I value)
Definition mpreal.hpp:98
mpreal(mpreal const &other, Precision p)
Convert to finite precision, or retain an already-exact value in exact mode.
Definition mpreal.hpp:153
mpreal(uninitialized_t) noexcept
Construct an unset placeholder for overwrite storage.
Definition mpreal.hpp:79
void copy_to(mpfr_ptr destination) const
Round into an initialized MPFR destination at its existing precision.
Definition mpreal.hpp:198
friend mpreal epsilon(Precision precision)
Spacing above one for a specified binary working precision.
Definition mpreal.hpp:805
mpreal & operator/=(mpreal const &rhs)
Definition mpreal.hpp:580
bool initialized() const noexcept
Whether this object holds an exact or approximate numerical value.
Definition mpreal.hpp:174
Precision precision() const
Return exact state or the working precision stored by MPFR.
Definition mpreal.hpp:178
void swap(mpreal &other) noexcept
Definition mpreal.hpp:172
exact_rational const & exact_value() const
Borrow the exact rational; valid while this object retains its state.
Definition mpreal.hpp:191
mpreal & operator*=(mpreal const &rhs)
Definition mpreal.hpp:579
mpreal(decimal_literal value)
Definition mpreal.hpp:95
bool empty() const noexcept
Return whether this document has no spans.
Definition presentation.cpp:1182
locale_t value
Definition data_table.cpp:49
presentation::styled_text text
Definition display.cpp:141
auto operator-(Async< T > const &x)
Unary negation overload for Async values.
Definition async_ops.hpp:477
uni20::complex< T > conj(detail::standard_complex< T > x)
Returns the complex conjugate for complex-valued scalars.
Definition math.hpp:70
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Definition qnum.hpp:403
Var< T > operator*(T x, Var< T > y)
Computes x * y with reverse-mode gradient propagation.
Definition var_toys.hpp:176
Var< T > operator+(Var< T > x, T y)
Computes x + y with reverse-mode gradient propagation.
Definition var_toys.hpp:136
Definition cli.cpp:6
Definition data_table.cpp:8
Definition async/async.hpp:26
mpcomplex acosh(mpcomplex const &z)
Principal inverse hyperbolic cosine, with imaginary signed zero selecting the cut side.
Definition mpcomplex.hpp:514
constexpr auto pi
Pi as a native constant or a precision-aware MPFR descriptor.
Definition math_constants.hpp:37
bool isnan(mpcomplex const &z)
Definition mpcomplex.hpp:499
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
constexpr auto operator*(basic_half_int< T > lhs, U rhs) -> basic_half_int< T >
Multiply a half-integer value by an integral scalar.
Definition half_int.hpp:367
mpreal tan(mpreal const &x)
Definition mpreal.hpp:657
auto operator<<(std::ostream &out, basic_half_int< T > value) -> std::ostream &
Stream a half-integer in integer or .5 decimal form.
Definition half_int.hpp:515
mpcomplex sinh(mpcomplex const &z)
Hyperbolic sine at the input precision; exact zero remains exact.
Definition mpcomplex.hpp:509
constexpr auto abs(basic_half_int< T > value) -> basic_half_int< T >
Return the absolute value of a half-integer.
Definition half_int.hpp:431
mpreal expm1(mpreal const &x)
Exponential minus one without cancellation; retain input precision or exact zero.
Definition mpreal.hpp:722
mpreal atan(mpreal const &x)
Definition mpreal.hpp:658
bool operator==(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:284
exact_rational sqrt(X const &value)
Return the nonnegative rational square root, or require finite precision.
Definition exact_rational.hpp:355
mpcomplex log(mpcomplex const &z)
Principal logarithm, with the sign of imaginary zero selecting the cut side.
Definition mpcomplex.hpp:505
bool isinf(mpcomplex const &z)
Definition mpcomplex.hpp:500
mpreal acos(mpreal const &x)
Inverse cosine in radians; retain input precision or a supported exact result.
Definition mpreal.hpp:757
constexpr auto operator-(basic_half_int< T > lhs, basic_half_int< T > rhs) -> basic_half_int< T >
Subtract two half-integer values.
Definition half_int.hpp:345
mpcomplex cosh(mpcomplex const &z)
Hyperbolic cosine at the input precision; exact zero returns exact one.
Definition mpcomplex.hpp:511
mpreal tanh(mpreal const &x)
Hyperbolic tangent; retain input precision or a supported exact result.
Definition mpreal.hpp:778
mpreal log2(mpreal const &x)
Base-two logarithm; retain input precision or a supported exact result.
Definition mpreal.hpp:729
mpreal cbrt(mpreal const &x)
Real cube root, including negative arguments; retain input precision or a supported exact result.
Definition mpreal.hpp:708
mpreal exp2(mpreal const &x)
Base-two exponential; retain input precision or a supported exact result.
Definition mpreal.hpp:715
mpreal log10(mpreal const &x)
Base-ten logarithm; retain input precision or a supported exact result.
Definition mpreal.hpp:736
constexpr bool is_exact(detail::standard_complex< T > const &x)
A standard complex value is exact only when both components are exact.
Definition math.hpp:50
mpreal epsilon(Precision precision)
Spacing above one for a specified binary working precision.
Definition mpreal.hpp:805
constexpr Precision common_precision(Precision a, Precision b)
Combine precisions, treating exact values as neutral and rejecting unequal finite precisions.
Definition core/precision.hpp:57
std::optional< exact_rational > rational_root(exact_rational const &x, unsigned long degree)
Return a rational nth root when it exists, without choosing a precision.
Definition exact_rational.hpp:296
mpreal hypot(mpreal const &x, mpreal const &y)
Definition mpreal.hpp:660
bool signbit(mpreal const &x)
Definition mpreal.hpp:642
constexpr auto euler_gamma
Euler-Mascheroni constant, with the same type and precision rules as pi.
Definition math_constants.hpp:47
exact_rational pow(X const &base, Y const &power)
Evaluate a rational power exactly when its real result is rational.
Definition exact_rational.hpp:337
mpreal asinh(mpreal const &x)
Inverse hyperbolic sine; retain input precision or a supported exact result.
Definition mpreal.hpp:785
mpreal asin(mpreal const &x)
Inverse sine in radians; retain input precision or a supported exact result.
Definition mpreal.hpp:750
constexpr auto catalan
Catalan constant as a native value or a precision-aware MPFR descriptor.
Definition math_constants.hpp:52
constexpr auto log_two
Natural logarithm of two, with the same type and precision rules as pi.
Definition math_constants.hpp:42
mpreal remainder(mpreal const &a, mpreal const &b)
Nearest-even-quotient remainder; exact operands retain exact rational results.
Definition mpreal_functions.hpp:534
mpreal atanh(mpreal const &x)
Inverse hyperbolic tangent; retain input precision or a supported exact result.
Definition mpreal.hpp:799
mpcomplex exp(mpcomplex const &z)
Definition mpcomplex.hpp:503
mpreal atan2(mpreal const &y, mpreal const &x)
Definition mpreal.hpp:659
constexpr auto operator/(basic_half_int< T > lhs, U rhs) -> U
Divide a half-integer value by a floating-point scalar.
Definition half_int.hpp:422
mpcomplex sin(mpcomplex const &z)
Definition mpcomplex.hpp:506
std::strong_ordering operator<=>(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:288
mpreal log1p(mpreal const &x)
Logarithm of one plus x without cancellation; retain input precision or exact zero.
Definition mpreal.hpp:743
mpcomplex cos(mpcomplex const &z)
Definition mpcomplex.hpp:507
static mpfr_prec_t digits(mpreal const &x)
Definition mpreal.hpp:310
static mpreal epsilon(mpreal const &x)
Definition mpreal.hpp:315
static mpreal epsilon()=delete
static mpfr_prec_t digits(Precision p)
Significand bits at a finite working precision.
Definition mpreal.hpp:309
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16
Request storage whose numerical values must be written before being read.
Definition common/initialization.hpp:17