Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
mpreal_functions.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "math_results.hpp"
5#include "mpreal.hpp"
6#include <cstdint>
7
8namespace uni20
9{
10namespace detail
11{
12enum class integer_rounding
13{
14 down,
15 up,
16 toward_zero,
17 away_ties,
18 even_ties
19};
20
21inline exact_rational round_rational(exact_rational const& x, integer_rounding mode)
22{
23 return mpreal_access::rational_result([&](mpq_ptr result) {
24 auto q = x.native_handle();
25 auto numerator = mpq_numref(q), denominator = mpq_denref(q);
26 auto integer = mpq_numref(result);
27 if (mode == integer_rounding::down)
28 mpz_fdiv_q(integer, numerator, denominator);
29 else if (mode == integer_rounding::up)
30 mpz_cdiv_q(integer, numerator, denominator);
31 else
32 {
33 mp_integer remainder;
34 mpz_tdiv_qr(integer, remainder.value, numerator, denominator);
35 if (mode == integer_rounding::toward_zero) return;
36 mpz_abs(remainder.value, remainder.value);
37 mpz_mul_2exp(remainder.value, remainder.value, 1);
38 int comparison = mpz_cmp(remainder.value, denominator);
39 if (comparison > 0 || (comparison == 0 && (mode == integer_rounding::away_ties || mpz_odd_p(integer))))
40 {
41 if (mpz_sgn(numerator) < 0)
42 mpz_sub_ui(integer, integer, 1);
43 else
44 mpz_add_ui(integer, integer, 1);
45 }
46 }
47 });
48}
49
50template <integer_rounding Mode, auto Operation> mpreal round_integer(mpreal const& x, Precision p)
51{
52 (void)x.precision();
53 if (x.is_exact()) return mpreal(round_rational(x.exact_value(), Mode), p);
54 return mpreal_access::finite_result(p, [&](mpfr_ptr result) { Operation(result, x.native_handle(), MPFR_RNDN); });
55}
56} // namespace detail
57
59inline mpreal floor(mpreal const& x)
60{
61 return detail::round_integer<detail::integer_rounding::down, mpfr_rint_floor>(x, x.precision());
62}
65inline mpreal floor(mpreal const& x, Precision p)
66{
67 (void)p.bit_count();
68 return detail::round_integer<detail::integer_rounding::down, mpfr_rint_floor>(x, p);
69}
70
72inline mpreal ceil(mpreal const& x)
73{
74 return detail::round_integer<detail::integer_rounding::up, mpfr_rint_ceil>(x, x.precision());
75}
78inline mpreal ceil(mpreal const& x, Precision p)
79{
80 (void)p.bit_count();
81 return detail::round_integer<detail::integer_rounding::up, mpfr_rint_ceil>(x, p);
82}
83
85inline mpreal trunc(mpreal const& x)
86{
87 return detail::round_integer<detail::integer_rounding::toward_zero, mpfr_rint_trunc>(x, x.precision());
88}
91inline mpreal trunc(mpreal const& x, Precision p)
92{
93 (void)p.bit_count();
94 return detail::round_integer<detail::integer_rounding::toward_zero, mpfr_rint_trunc>(x, p);
95}
96
98inline mpreal round(mpreal const& x)
99{
100 return detail::round_integer<detail::integer_rounding::away_ties, mpfr_rint_round>(x, x.precision());
101}
104inline mpreal round(mpreal const& x, Precision p)
105{
106 (void)p.bit_count();
107 return detail::round_integer<detail::integer_rounding::away_ties, mpfr_rint_round>(x, p);
108}
109
111inline mpreal round_even(mpreal const& x)
112{
113 return detail::round_integer<detail::integer_rounding::even_ties, mpfr_rint_roundeven>(x, x.precision());
114}
118{
119 (void)p.bit_count();
120 return detail::round_integer<detail::integer_rounding::even_ties, mpfr_rint_roundeven>(x, p);
121}
122
123namespace detail
124{
125inline mpreal fused_product_sum(mpreal const& a, mpreal const& b, mpreal const& c, Precision p)
126{
127 (void)a.precision();
128 (void)b.precision();
129 (void)c.precision();
130 if (p.is_exact()) return mpreal(a.exact_value() * b.exact_value() + c.exact_value());
131 if (!a.is_exact() && !b.is_exact() && !c.is_exact())
132 return mpreal_access::finite_result(p, [&](mpfr_ptr result) {
133 mpfr_fma(result, a.native_handle(), b.native_handle(), c.native_handle(), MPFR_RNDN);
134 });
135 if (!isfinite(a) || !isfinite(b) || !isfinite(c))
136 {
137 // Nonfinite results depend only on the exact operands' signs and zeros.
138 // Materializing a huge rational here could manufacture an extra infinity.
139 auto marker = [p](mpreal const& x) {
140 return x.is_exact() ? mpreal(mpq_sgn(x.exact_value().native_handle()), p) : x;
141 };
142 auto av = marker(a), bv = marker(b), cv = marker(c);
143 return mpreal_access::finite_result(p, [&](mpfr_ptr result) {
144 mpfr_fma(result, av.native_handle(), bv.native_handle(), cv.native_handle(), MPFR_RNDN);
145 });
146 }
147 return mpreal_access::finite_result(p, [&](mpfr_ptr result) {
148 if ((a == 0 || b == 0) && c == 0)
149 {
150 bool negative = (signbit(a) != signbit(b)) && signbit(c);
151 mpfr_set_zero(result, negative ? -1 : 1);
152 return;
153 }
154 scaled_rational_ratio({mpreal_access::stored_scaled(a) * mpreal_access::stored_scaled(b),
155 mpreal_access::stored_scaled(c),
156 {exact_rational{1}, 0},
157 {}})
158 .round_to(result);
159 });
160}
161
162inline mpreal binary_scale(mpreal const& x, std::int64_t exponent, Precision p)
163{
164 (void)x.precision();
165 if (p.is_exact())
166 {
167 return mpreal(mpreal_access::rational_result([&](mpq_ptr q) {
168 mpq_set(q, x.exact_value().native_handle());
169 if (mpq_sgn(q) == 0) return;
170 if (exponent >= 0)
171 mpq_mul_2exp(q, q, static_cast<mp_bitcnt_t>(exponent));
172 else
173 mpq_div_2exp(q, q, static_cast<mp_bitcnt_t>(std::uint64_t(0) - std::uint64_t(exponent)));
174 }));
175 }
176 return mpreal_access::finite_result(p, [&](mpfr_ptr result) {
177 if (x.is_exact())
178 {
179 scaled_rational_ratio({scaled_rational{x.exact_value(), exponent}, {}, {exact_rational{1}, 0}, {}})
180 .round_to(result);
181 }
182 else
183 {
184 static_assert(sizeof(long) >= sizeof(std::int64_t));
185 mpfr_mul_2si(result, x.native_handle(), static_cast<long>(exponent), MPFR_RNDN);
186 }
187 });
188}
189} // namespace detail
190
192inline mpreal fma(mpreal const& a, mpreal const& b, mpreal const& c)
193{
194 return detail::fused_product_sum(a, b, c,
195 common_precision(common_precision(a.precision(), b.precision()), c.precision()));
196}
198inline mpreal fma(mpreal const& a, mpreal const& b, mpreal const& c, Precision p)
199{
200 (void)p.bit_count();
201 return detail::fused_product_sum(a, b, c, p);
202}
203
205template <detail::MathInteger I> inline mpreal ldexp(mpreal const& x, I exponent)
206{
207 return detail::binary_scale(x, detail::math_integer(exponent), x.precision());
208}
210template <detail::MathInteger I> inline mpreal ldexp(mpreal const& x, I exponent, Precision p)
211{
212 (void)p.bit_count();
213 return detail::binary_scale(x, detail::math_integer(exponent), p);
214}
216template <detail::MathInteger I> inline mpreal scalbn(mpreal const& x, I exponent) { return ldexp(x, exponent); }
218template <detail::MathInteger I> inline mpreal scalbn(mpreal const& x, I exponent, Precision p)
219{
220 return ldexp(x, exponent, p);
221}
222
224inline mpreal copysign(mpreal const& magnitude, mpreal const& sign)
225{
226 auto p = magnitude.precision();
227 bool negative = signbit(sign);
228 if (p.is_exact())
229 {
230 auto q = magnitude.exact_value();
231 if ((q < 0) != negative) q = -q;
232 return mpreal(q);
233 }
234 return detail::mpreal_access::finite_result(
235 p, [&](mpfr_ptr result) { mpfr_setsign(result, magnitude.native_handle(), negative, MPFR_RNDN); });
236}
238inline mpreal copysign(mpreal const& magnitude, mpreal const& sign, Precision p)
239{
240 (void)p.bit_count();
241 return copysign(magnitude.at(p), sign);
242}
243
244namespace detail
245{
246inline mpreal select_extreme(mpreal const& a, mpreal const& b, Precision p, bool maximum)
247{
248 (void)a.precision();
249 (void)b.precision();
250 if (isnan(a)) return b.at(p);
251 if (isnan(b)) return a.at(p);
252 if (a == 0 && b == 0 && !p.is_exact())
253 return mpreal_access::finite_result(p, [&](mpfr_ptr result) {
254 bool negative = maximum ? signbit(a) && signbit(b) : signbit(a) || signbit(b);
255 mpfr_set_zero(result, negative ? -1 : 1);
256 });
257 return ((maximum ? a > b : a < b) ? a : b).at(p);
258}
259} // namespace detail
261inline mpreal fmin(mpreal const& a, mpreal const& b)
262{
263 return detail::select_extreme(a, b, common_precision(a.precision(), b.precision()), false);
264}
266inline mpreal fmin(mpreal const& a, mpreal const& b, Precision p)
267{
268 (void)p.bit_count();
269 return detail::select_extreme(a, b, p, false);
270}
272inline mpreal fmax(mpreal const& a, mpreal const& b)
273{
274 return detail::select_extreme(a, b, common_precision(a.precision(), b.precision()), true);
275}
277inline mpreal fmax(mpreal const& a, mpreal const& b, Precision p)
278{
279 (void)p.bit_count();
280 return detail::select_extreme(a, b, p, true);
281}
282namespace detail
283{
284inline mpreal positive_difference(mpreal const& a, mpreal const& b, Precision p)
285{
286 (void)a.precision();
287 (void)b.precision();
288 if (isnan(a) || isnan(b)) return mpreal("nan", p);
289 if (a <= b) return mpreal(p);
290 return fused_product_sum(a, mpreal{1}, -b, p);
291}
292} // namespace detail
294inline mpreal fdim(mpreal const& a, mpreal const& b)
295{
296 return detail::positive_difference(a, b, common_precision(a.precision(), b.precision()));
297}
299inline mpreal fdim(mpreal const& a, mpreal const& b, Precision p)
300{
301 (void)p.bit_count();
302 return detail::positive_difference(a, b, p);
303}
304
306inline mpreal next_up(mpreal const& x)
307{
308 return detail::mpreal_access::finite_result(x.precision(), [&](mpfr_ptr result) {
309 mpfr_set(result, x.native_handle(), MPFR_RNDN);
310 mpfr_nextabove(result);
311 });
312}
314inline mpreal next_up(mpreal const& x, Precision p)
315{
316 (void)p.bit_count();
317 return next_up(x.at(p));
318}
320inline mpreal next_down(mpreal const& x)
321{
322 return detail::mpreal_access::finite_result(x.precision(), [&](mpfr_ptr result) {
323 mpfr_set(result, x.native_handle(), MPFR_RNDN);
324 mpfr_nextbelow(result);
325 });
326}
328inline mpreal next_down(mpreal const& x, Precision p)
329{
330 (void)p.bit_count();
331 return next_down(x.at(p));
332}
335inline mpreal nextafter(mpreal const& x, mpreal const& direction)
336{
337 auto p = x.precision();
338 (void)p.bit_count();
339 (void)direction.precision();
340 if (isnan(x) || isnan(direction)) return mpreal("nan", p);
341 if (x == direction) return copysign(x, direction);
342 return x < direction ? next_up(x) : next_down(x);
343}
345inline mpreal nextafter(mpreal const& x, mpreal const& direction, Precision p)
346{
347 (void)p.bit_count();
348 return nextafter(x.at(p), direction);
349}
350
351namespace detail
352{
353inline std::int64_t rational_binary_exponent(exact_rational const& x)
354{
355 auto q = x.native_handle();
356 if (mpq_sgn(q) == 0) return 0;
357 mp_integer numerator, denominator;
358 mpz_abs(numerator.value, mpq_numref(q));
359 mpz_set(denominator.value, mpq_denref(q));
360 auto exponent = static_cast<std::int64_t>(mpz_sizeinbase(numerator.value, 2)) -
361 static_cast<std::int64_t>(mpz_sizeinbase(denominator.value, 2));
362 if (exponent >= 0)
363 mpz_mul_2exp(denominator.value, denominator.value, exponent);
364 else
365 mpz_mul_2exp(numerator.value, numerator.value, -exponent);
366 return exponent + (mpz_cmp(numerator.value, denominator.value) >= 0 ? 1 : 0);
367}
368inline frexp_result<mpreal> decompose_binary(mpreal const& x, Precision p)
369{
370 (void)x.precision();
371 if (x.is_exact())
372 {
373 auto exponent = rational_binary_exponent(x.exact_value());
374 auto fraction = binary_scale(x, -exponent, p);
375 // Rounding the normalized rational can carry into the next binade.
376 if (!p.is_exact() && abs(fraction) == 1)
377 {
378 fraction = binary_scale(fraction, -1, p);
379 ++exponent;
380 }
381 return {std::move(fraction), exponent};
382 }
383 if (!isfinite(x)) return {x.at(p), 0};
384 mpfr_exp_t exponent = 0;
385 auto fraction = mpreal_access::finite_result(
386 p, [&](mpfr_ptr result) { mpfr_frexp(&exponent, result, x.native_handle(), MPFR_RNDN); });
387 return {std::move(fraction), exponent};
388}
389inline modf_result<mpreal> decompose_fraction(mpreal const& x, Precision p)
390{
391 (void)x.precision();
392 if (x.is_exact())
393 {
394 auto integer = round_rational(x.exact_value(), integer_rounding::toward_zero);
395 return {mpreal(x.exact_value() - integer, p), mpreal(integer, p)};
396 }
397 mpreal fraction(uninitialized);
398 auto integer = mpreal_access::finite_result(p, [&](mpfr_ptr whole) {
399 fraction =
400 mpreal_access::finite_result(p, [&](mpfr_ptr part) { mpfr_modf(whole, part, x.native_handle(), MPFR_RNDN); });
401 });
402 return {std::move(fraction), std::move(integer)};
403}
404
405struct scaled_remainder
406{
407 scaled_rational value;
408 int quotient;
409};
410// Integer modular arithmetic retains non-dyadic rational operands without
411// expanding binary exponents. The quotient is needed only modulo eight.
412inline scaled_remainder rational_remainder(scaled_rational const& a, scaled_rational const& b, bool nearest)
413{
414 auto aq = a.coefficient.native_handle(), bq = b.coefficient.native_handle();
415 if (mpq_sgn(bq) == 0) throw std::domain_error("exact remainder: zero divisor");
416 mp_integer numerator, divisor, remainder, quotient, modulus, factor;
417 mpz_mul(numerator.value, mpq_numref(aq), mpq_denref(bq));
418 mpz_abs(numerator.value, numerator.value);
419 mpz_mul(divisor.value, mpq_numref(bq), mpq_denref(aq));
420 mpz_abs(divisor.value, divisor.value);
421 auto difference = a.exponent - b.exponent;
422 auto exponent = b.exponent;
423 if (difference >= 0)
424 {
425 mpz_mul_ui(modulus.value, divisor.value, 8);
426 mpz_set_ui(factor.value, 2);
427 mpz_powm_ui(factor.value, factor.value, static_cast<unsigned long>(difference), modulus.value);
428 mpz_mul(numerator.value, numerator.value, factor.value);
429 mpz_mod(numerator.value, numerator.value, modulus.value);
430 }
431 else
432 {
433 exponent = a.exponent;
434 mpz_neg(factor.value, divisor.value);
435 if (nearest) mpz_mul_2exp(numerator.value, numerator.value, 1);
436 int comparison = scaled_sum_sign<2>({numerator.value, factor.value}, {0, -difference});
437 if (nearest) mpz_tdiv_q_2exp(numerator.value, numerator.value, 1);
438 if (comparison < 0 || (nearest && comparison == 0)) return {a, 0};
439 // The shifted divisor is now bounded by twice the existing numerator size.
440 mpz_mul_2exp(divisor.value, divisor.value, static_cast<mp_bitcnt_t>(-difference));
441 }
442 mpz_tdiv_qr(quotient.value, remainder.value, numerator.value, divisor.value);
443 auto quotient_bits = static_cast<int>(mpz_fdiv_ui(quotient.value, 8));
444 if (nearest)
445 {
446 mpz_mul_2exp(factor.value, remainder.value, 1);
447 int comparison = mpz_cmp(factor.value, divisor.value);
448 if (comparison > 0 || (comparison == 0 && (quotient_bits & 1)))
449 {
450 mpz_sub(remainder.value, remainder.value, divisor.value);
451 quotient_bits = (quotient_bits + 1) % 8;
452 }
453 }
454 if (mpq_sgn(aq) < 0) mpz_neg(remainder.value, remainder.value);
455 if ((mpq_sgn(aq) < 0) != (mpq_sgn(bq) < 0)) quotient_bits = -quotient_bits;
456 auto coefficient = mpreal_access::rational_result([&](mpq_ptr q) {
457 mpz_set(mpq_numref(q), remainder.value);
458 mpz_mul(mpq_denref(q), mpq_denref(aq), mpq_denref(bq));
459 });
460 return {{std::move(coefficient), exponent}, quotient_bits};
461}
462inline remquo_result<mpreal> remainder_value(mpreal const& a, mpreal const& b, Precision p, bool nearest)
463{
464 (void)a.precision();
465 (void)b.precision();
466 if (p.is_exact())
467 {
468 auto result = rational_remainder(mpreal_access::stored_scaled(a), mpreal_access::stored_scaled(b), nearest);
469 return {mpreal(result.value.coefficient), result.quotient};
470 }
471 if (!isfinite(a) || isnan(b) || b == 0) return {mpreal("nan", p), 0};
472 if (isinf(b)) return {a.at(p), 0};
473 if (!a.is_exact() && !b.is_exact())
474 {
475 long quotient = 0;
476 auto result = mpreal_access::finite_result(p, [&](mpfr_ptr out) {
477 if (nearest)
478 mpfr_remquo(out, &quotient, a.native_handle(), b.native_handle(), MPFR_RNDN);
479 else
480 mpfr_fmodquo(out, &quotient, a.native_handle(), b.native_handle(), MPFR_RNDN);
481 });
482 return {std::move(result), static_cast<int>(quotient % 8)};
483 }
484 auto result = rational_remainder(mpreal_access::stored_scaled(a), mpreal_access::stored_scaled(b), nearest);
485 auto value = mpreal_access::finite_result(p, [&](mpfr_ptr out) {
486 if (result.value.coefficient == 0)
487 mpfr_set_zero(out, signbit(a) ? -1 : 1);
488 else
489 scaled_rational_ratio({result.value, {}, {exact_rational{1}, 0}, {}}).round_to(out);
490 });
491 return {std::move(value), result.quotient};
492}
493} // namespace detail
494
498inline frexp_result<mpreal> frexp(mpreal const& x) { return detail::decompose_binary(x, x.precision()); }
500inline frexp_result<mpreal> frexp(mpreal const& x, Precision p)
501{
502 (void)p.bit_count();
503 return detail::decompose_binary(x, p);
504}
506inline modf_result<mpreal> modf(mpreal const& x) { return detail::decompose_fraction(x, x.precision()); }
508inline modf_result<mpreal> modf(mpreal const& x, Precision p)
509{
510 (void)p.bit_count();
511 return detail::decompose_fraction(x, p);
512}
515inline std::int64_t ilogb(mpreal const& x)
516{
517 if (!isfinite(x) || x == 0) throw std::domain_error("ilogb requires a finite nonzero value");
518 return x.is_exact() ? detail::rational_binary_exponent(x.exact_value()) - 1 : mpfr_get_exp(x.native_handle()) - 1;
519}
520
522inline mpreal fmod(mpreal const& a, mpreal const& b)
523{
524 return detail::remainder_value(a, b, common_precision(a.precision(), b.precision()), false).remainder;
525}
527inline mpreal fmod(mpreal const& a, mpreal const& b, Precision p)
528{
529 (void)p.bit_count();
530 return detail::remainder_value(a, b, p, false).remainder;
531}
532
534inline mpreal remainder(mpreal const& a, mpreal const& b)
535{
536 return detail::remainder_value(a, b, common_precision(a.precision(), b.precision()), true).remainder;
537}
539inline mpreal remainder(mpreal const& a, mpreal const& b, Precision p)
540{
541 (void)p.bit_count();
542 return detail::remainder_value(a, b, p, true).remainder;
543}
544
546inline remquo_result<mpreal> remquo(mpreal const& a, mpreal const& b)
547{
548 return detail::remainder_value(a, b, common_precision(a.precision(), b.precision()), true);
549}
551inline remquo_result<mpreal> remquo(mpreal const& a, mpreal const& b, Precision p)
552{
553 (void)p.bit_count();
554 return detail::remainder_value(a, b, p, true);
555}
556
557namespace detail
558{
559template <bool Hyperbolic> auto paired_functions(mpreal const& x)
560{
561 using result_type = std::conditional_t<Hyperbolic, sinhcosh_result<mpreal>, sincos_result<mpreal>>;
562 auto p = x.precision();
563 if (x.is_exact())
564 {
565 if constexpr (Hyperbolic)
566 return result_type{sinh(x), cosh(x)};
567 else
568 return result_type{sin(x), cos(x)};
569 }
570 mpreal cosine(uninitialized);
571 auto sine = mpreal_access::finite_result(p, [&](mpfr_ptr s) {
572 cosine = mpreal_access::finite_result(p, [&](mpfr_ptr c) {
573 if constexpr (Hyperbolic)
574 mpfr_sinh_cosh(s, c, x.native_handle(), MPFR_RNDN);
575 else
576 mpfr_sin_cos(s, c, x.native_handle(), MPFR_RNDN);
577 });
578 });
579 return result_type{std::move(sine), std::move(cosine)};
580}
581} // namespace detail
583inline sincos_result<mpreal> sincos(mpreal const& x) { return detail::paired_functions<false>(x); }
585inline sincos_result<mpreal> sincos(mpreal const& x, Precision p)
586{
587 (void)p.bit_count();
588 return sincos(x.at(p));
589}
591inline sinhcosh_result<mpreal> sinhcosh(mpreal const& x) { return detail::paired_functions<true>(x); }
593inline sinhcosh_result<mpreal> sinhcosh(mpreal const& x, Precision p)
594{
595 (void)p.bit_count();
596 return sinhcosh(x.at(p));
597}
598
599namespace detail
600{
601// The callers remove rational-result cases first. The remaining result is
602// irrational/non-dyadic and cannot equal a binary rounding boundary, so an
603// enclosing interval eventually selects one rounded value.
604template <class F> mpreal rational_monotone(exact_rational const& positive, Precision p, bool decreasing, F operation)
605{
606 auto bits = std::max<mpfr_prec_t>(64, p.bit_count());
607 while (true)
608 {
609 auto work = Precision::bits(bits);
610 mpfr_value lower(work), upper(work);
611 mpfr_set_q(lower.value, positive.native_handle(), MPFR_RNDD);
612 mpfr_set_q(upper.value, positive.native_handle(), MPFR_RNDU);
613 if (mpfr_zero_p(lower.value) || mpfr_inf_p(upper.value))
614 throw std::overflow_error("rational power/root input exceeds the MPFR exponent range");
615 if (decreasing) mpfr_swap(lower.value, upper.value);
616 operation(lower.value, lower.value, MPFR_RNDD);
617 operation(upper.value, upper.value, MPFR_RNDU);
618 auto lo = mpreal_access::finite_result(p, [&](mpfr_ptr out) { mpfr_set(out, lower.value, MPFR_RNDN); });
619 auto hi = mpreal_access::finite_result(p, [&](mpfr_ptr out) { mpfr_set(out, upper.value, MPFR_RNDN); });
620 if (lo == hi) return lo;
621 if (bits > MPFR_PREC_MAX / 2) throw std::overflow_error("rational power/root needs unsupported working precision");
622 bits *= 2;
623 }
624}
625inline mpreal integer_power_value(mpreal const& x, std::int64_t exponent, Precision p)
626{
627 (void)x.precision();
628 if (p.is_exact()) return mpreal(uni20::pow(x.exact_value(), exact_rational(exponent)));
629 if (!x.is_exact())
630 {
631 auto n = exact_rational(exponent);
632 return mpreal_access::finite_result(
633 p, [&](mpfr_ptr out) { mpfr_pow_z(out, x.native_handle(), mpq_numref(n.native_handle()), MPFR_RNDN); });
634 }
635 if (exponent == 0) return mpreal(1, p);
636 if (x == 0) return exponent < 0 ? mpreal("inf", p) : mpreal(0, p);
637 auto q = x.exact_value();
638 if (exponent < 0) q = exact_rational{1} / q;
639 bool negative = q < 0 && exponent % 2 != 0;
640 if (q < 0) q = -q;
641 auto magnitude = exponent < 0 ? std::uint64_t(0) - std::uint64_t(exponent) : std::uint64_t(exponent);
642 auto n = exact_rational(magnitude);
643 auto operation = [&](mpfr_ptr out, mpfr_srcptr in, mpfr_rnd_t rounding) {
644 mpfr_pow_z(out, in, mpq_numref(n.native_handle()), rounding);
645 };
646 mpreal result(uninitialized);
647 if (mpz_popcount(mpq_denref(q.native_handle())) == 1)
648 {
649 // A dyadic base is exactly representable. Direct evaluation also resolves
650 // exact halfway results that an interval test could not separate.
651 auto bits = std::max<mpfr_prec_t>(MPFR_PREC_MIN, mpz_sizeinbase(mpq_numref(q.native_handle()), 2));
652 auto input = mpreal(q, Precision::bits(bits));
653 if (!isfinite(input) || input == 0) throw std::overflow_error("power input exceeds the MPFR exponent range");
654 result = mpreal_access::finite_result(p, [&](mpfr_ptr out) { operation(out, input.native_handle(), MPFR_RNDN); });
655 }
656 else
657 result = rational_monotone(q, p, false, operation);
658 return negative ? -result : result;
659}
660inline mpreal integer_root_value(mpreal const& x, std::int64_t degree, Precision p)
661{
662 (void)x.precision();
663 if (degree == 0)
664 {
665 if (p.is_exact()) throw std::domain_error("exact root: zeroth root");
666 return mpreal("nan", p);
667 }
668 static_assert(sizeof(long) >= sizeof(std::int64_t));
669 if (!x.is_exact())
670 return mpreal_access::finite_result(
671 p, [&](mpfr_ptr out) { mpfr_rootn_si(out, x.native_handle(), static_cast<long>(degree), MPFR_RNDN); });
672 auto magnitude = degree < 0 ? std::uint64_t(0) - std::uint64_t(degree) : std::uint64_t(degree);
673 if (auto root = rational_root(x.exact_value(), static_cast<unsigned long>(magnitude)))
674 {
675 if (degree < 0)
676 {
677 if (*root == 0 && !p.is_exact()) return mpreal("inf", p);
678 *root = exact_rational{1} / *root;
679 }
680 return mpreal(*root, p);
681 }
682 if (p.is_exact()) throw std::logic_error("rootn requires finite working precision for an irrational result");
683 auto q = x.exact_value();
684 bool negative = q < 0;
685 if (negative && degree % 2 == 0) return mpreal("nan", p);
686 if (negative) q = -q;
687 auto result = rational_monotone(q, p, degree < 0, [&](mpfr_ptr out, mpfr_srcptr in, mpfr_rnd_t rounding) {
688 mpfr_rootn_si(out, in, static_cast<long>(degree), rounding);
689 });
690 return negative ? -result : result;
691}
692} // namespace detail
693
695template <detail::MathInteger I> inline mpreal pown(mpreal const& x, I exponent)
696{
697 return detail::integer_power_value(x, detail::math_integer(exponent), x.precision());
698}
700template <detail::MathInteger I> inline mpreal pown(mpreal const& x, I exponent, Precision p)
701{
702 (void)p.bit_count();
703 return detail::integer_power_value(x, detail::math_integer(exponent), p);
704}
708template <detail::MathInteger I> inline mpreal rootn(mpreal const& x, I degree)
709{
710 return detail::integer_root_value(x, detail::math_integer(degree), x.precision());
711}
713template <detail::MathInteger I> inline mpreal rootn(mpreal const& x, I degree, Precision p)
714{
715 (void)p.bit_count();
716 return detail::integer_root_value(x, detail::math_integer(degree), p);
717}
718
719namespace detail
720{
721inline mpreal compound_value(mpreal const& x, std::int64_t n, Precision p)
722{
723 (void)x.precision();
724 if (x.is_exact())
725 {
726 if (x < -1)
727 {
728 if (p.is_exact()) throw std::domain_error("compound requires x >= -1");
729 return mpreal("nan", p);
730 }
731 return integer_power_value(mpreal(x.exact_value() + exact_rational{1}), n, p);
732 }
733 static_assert(sizeof(long) >= sizeof(std::int64_t));
734 return mpreal_access::finite_result(
735 p, [&](mpfr_ptr out) { mpfr_compound_si(out, x.native_handle(), static_cast<long>(n), MPFR_RNDN); });
736}
737} // namespace detail
741template <detail::MathInteger I> inline mpreal compound(mpreal const& x, I n)
742{
743 return detail::compound_value(x, detail::math_integer(n), x.precision());
744}
746template <detail::MathInteger I> inline mpreal compound(mpreal const& x, I n, Precision p)
747{
748 (void)p.bit_count();
749 return detail::compound_value(x, detail::math_integer(n), p);
750}
751} // namespace uni20
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
Owning exact rational or finite-precision MPFR real, with eager arithmetic.
Definition mpreal.hpp:74
mpreal at(Precision p) const
Definition mpreal.hpp:206
mpfr_srcptr native_handle() const
Borrow an approximate MPFR value; exact and unset states require explicit materialization first.
Definition mpreal.hpp:184
bool is_exact() const noexcept
Whether the active payload is an exact rational.
Definition mpreal.hpp:176
Precision precision() const
Return exact state or the working precision stored by MPFR.
Definition mpreal.hpp:178
exact_rational const & exact_value() const
Borrow the exact rational; valid while this object retains its state.
Definition mpreal.hpp:191
locale_t value
Definition data_table.cpp:49
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Definition qnum.hpp:403
Definition cli.cpp:6
DiagnosticMagnitude< R > magnitude(R const &value)
Definition gmres_diagnostics.hpp:98
constexpr detail::scalar_math::isfinite_fn isfinite
Whether a real scalar is finite.
Definition math.hpp:519
constexpr detail::scalar_math::cos_fn cos
Cosine with scalar-specific precision handling.
Definition math.hpp:473
constexpr detail::scalar_math::signbit_fn signbit
Inspect the sign, including approximate negative zero.
Definition math.hpp:525
constexpr detail::scalar_math::sin_fn sin
Sine with scalar-specific precision handling.
Definition math.hpp:471
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
constexpr detail::scalar_math::isnan_fn isnan
Whether a real scalar is NaN.
Definition math.hpp:521
constexpr detail::scalar_math::remainder_fn remainder
Remainder using the nearest-even integer quotient.
Definition math.hpp:565
constexpr detail::scalar_math::isinf_fn isinf
Whether a real scalar is infinite.
Definition math.hpp:523
Definition data_table.cpp:8
Definition async/async.hpp:26
mpreal round_even(mpreal const &x)
Round to nearest integer, with ties to even; exact inputs produce exact integers.
Definition mpreal_functions.hpp:111
auto degree(QNum const &q) -> int
Return the integral degree of a quantum number.
Definition qnum.hpp:270
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
bool signbit(mpreal const &x)
Definition mpreal.hpp:642
mpreal round(mpreal const &x)
Round to nearest integer, with ties away from zero; exact inputs produce exact integers.
Definition mpreal_functions.hpp:98
mpreal trunc(mpreal const &x)
Round toward zero; exact inputs produce exact integers.
Definition mpreal_functions.hpp:85
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 ceil(mpreal const &x)
Round toward positive infinity; exact inputs produce exact integers.
Definition mpreal_functions.hpp:72
mpreal remainder(mpreal const &a, mpreal const &b)
Nearest-even-quotient remainder; exact operands retain exact rational results.
Definition mpreal_functions.hpp:534
mpreal floor(mpreal const &x)
Round toward negative infinity; exact inputs produce exact integers.
Definition mpreal_functions.hpp:59
A binary significand and exponent: value = fraction * 2^exponent.
Definition math_results.hpp:8
Signed fractional and integral parts of a real value.
Definition math_results.hpp:15
Nearest-even remainder and the signed low three bits of its quotient.
Definition math_results.hpp:22
Sine and cosine evaluated at one input and precision.
Definition math_results.hpp:29
Hyperbolic sine and cosine evaluated at one input and precision.
Definition math_results.hpp:36