5namespace uni20::detail::native_math::upper_gamma_guard
9template <NativeReal R>
struct two_word
13 two_word(R x) : hi(x) {}
14 two_word(R h, R l) : hi(h), lo(l) {}
15 static two_word
sum(R a, R b)
17 R s = a + b, v = s - a;
18 return {s, (a - (s - v)) + (b - v)};
20 friend two_word
operator+(two_word a, two_word b)
22 auto s = two_word::sum(a.hi, b.hi);
23 auto t = two_word::sum(a.lo, b.lo);
24 auto u = two_word::sum(s.lo, t.hi);
25 auto v = two_word::sum(s.hi, u.hi);
26 return two_word::sum(v.hi, v.lo + u.lo + t.lo);
28 friend two_word
operator-(two_word a) {
return {-a.hi, -a.lo}; }
29 friend two_word
operator-(two_word a, two_word b) {
return a + -b; }
30 friend two_word
operator*(two_word a, two_word b)
33 R e = std::fma(a.hi, b.hi, -
p);
34 e = std::fma(a.hi, b.lo, e);
35 e = std::fma(a.lo, b.hi, e);
36 e = std::fma(a.lo, b.lo, e);
37 return two_word::sum(
p, e);
39 friend two_word
operator/(two_word a, two_word b)
42 two_word result(q), r = a - b * result;
44 result = result + two_word(t);
46 return result + two_word(r.hi / b.hi);
48 two_word scaled(
int n)
const {
return {std::ldexp(hi, n), std::ldexp(lo, n)}; }
49 R rounded()
const {
return hi + lo; }
52template <NativeReal R> R epsilon()
54 constexpr int p = numeric_limits<R>::digits();
55 return std::ldexp(R(1), -
p - std::min(
p - 8, 32));
57template <NativeReal R> two_word<R>
log_two()
61 constexpr unsigned chunks[] = {0xb172, 0x17f7, 0xd1cf, 0x79ab, 0xc9e3, 0xb398, 0x03f2, 0xf6af,
62 0x40f3, 0x4326, 0x7298, 0xb62d, 0x8a0d, 0x175b, 0x8baa, 0xfa2b};
64 constexpr int n = (2 * numeric_limits<R>::digits() + 15) / 16;
65 for (
int i = 0; i < n; ++i)
66 s = s + two_word<R>(std::ldexp(R(chunks[i]), -16 * (i + 1)));
69template <NativeReal R> two_word<R>
log(R x)
71 using P = two_word<R>;
73 R m = std::frexp(x, &e);
79 P t = (P(m) - P(1)) / (P(m) + P(1));
80 P t2 = t * t, term = t, s = t;
81 for (
int k = 1; k < 2 * numeric_limits<R>::digits(); ++k)
84 P v = term / P(R(2 * k + 1));
86 if (std::abs(v.hi) < epsilon<R>() * std::abs(s.hi) / 16)
break;
88 return P(2) * s + P(R(e)) * log_two<R>();
90template <NativeReal R> R
exp(two_word<R> v)
92 using L = numeric_limits<R>;
93 using P = two_word<R>;
94 if (v.hi > R(2 * L::max_exponent))
return L::infinity();
95 if (v.hi < R(2 * (L::min_exponent - L::digits())))
return R(0);
97 int n = int(std::nearbyint(v.hi / ln2.hi));
98 P r = v - P(R(n)) * ln2, s(1), t(1);
99 for (
int k = 1; k < 2 * L::digits(); ++k)
103 if (std::abs(t.hi) < epsilon<R>() / 16)
break;
107 constexpr int denorm_exp = L::min_exponent - L::digits();
108 if (n < L::min_exponent)
110 P units = s.scaled(n - denorm_exp);
111 R q = std::floor(units.hi);
119 if (half.hi > 0 || (half.hi == 0 && half.lo > 0) || (half.hi == 0 && half.lo == 0 && std::fmod(q, R(2)) != 0))
121 return std::ldexp(q, denorm_exp);
123 return std::ldexp(s.rounded(), n);
127template <NativeReal R> two_word<R> fraction(R av, R xv)
129 using P = two_word<R>;
130 P a(av), x(xv), previous;
131 R eps = epsilon<R>();
132 if (av < -1 && xv / (-av - 1) < eps / 4)
return P(1) / (-a);
133 if (av <= R(-.5) && xv < eps * eps / 64)
return P(1) / (-a);
138 if (av < -1 && xv <= 1)
140 P term = P(1) / (-a),
sum = term;
141 for (
int n = 1; n < 2 * numeric_limits<R>::digits() && R(n) < -av; ++n)
143 P next = -term * x / (-a - P(R(n)));
144 if (std::abs(next.hi) <= eps * std::abs(
sum.hi) / 16)
return sum;
145 if (std::abs(next.hi) >= std::abs(term.hi))
break;
150 for (
int depth = 8; depth <= 128 * numeric_limits<R>::digits(); depth *= 2)
153 for (
int k = depth; k >= 1; --k)
154 value = (a - P(R(k))) * (P(R(k)) / (x - a + P(R(2 * k + 1)) +
value));
156 if (std::abs((
value - previous).hi) <= eps * std::abs(
value.hi))
return value;
159 return P(numeric_limits<R>::quiet_NaN());
163 using P = two_word<R>;
164 using L = numeric_limits<R>;
167 std::frexp(std::max(R(1), std::abs(lx.hi)), &shift);
171 P half = P(a / 2) * lx.scaled(-shift) - P(std::ldexp(x / 2, -shift));
172 R limit = std::ldexp(R(4 * L::max_exponent), -shift);
173 if (half.hi > limit)
return L::infinity();
174 if (half.hi < -limit)
return R(0);
175 half = half.scaled(shift);
176 P u = fraction(a, x);
177 if (std::isnan(u.hi))
return u.hi;
178 P lu =
log(u.hi) + P(u.lo) / P(u.hi);
179 return exp(P(2) * half + lu);
181template <NativeReal R> two_word<R> log_pair(two_word<R> x)
183 return log(x.hi) + two_word<R>(x.lo) / two_word<R>(x.hi);
185template <NativeReal R> two_word<R> log_two_pi()
187 constexpr unsigned chunks[] = {0xd67f, 0x1c86, 0x4beb, 0x4a69, 0x2979, 0x2002, 0x8832, 0x4047,
188 0x9f61, 0x1f1a, 0x268b, 0x169b, 0xbd8d, 0x4626, 0x7b54, 0x2aba};
190 constexpr int n = (2 * numeric_limits<R>::digits() + 15) / 16;
191 for (
int i = 0; i < n; ++i)
192 s = s + two_word<R>(std::ldexp(R(chunks[i]), -16 * (i + 1)));
195template <NativeReal R> two_word<R> log_gamma(R a)
197 using P = two_word<R>;
198 constexpr int p = numeric_limits<R>::digits(),
199 cutoff =
p <= 24 ? 32
206 constexpr long long nums[] = {1,
222 constexpr long long dens[] = {12, 360, 1260, 1680, 1188, 360360, 156, 122400,
223 244188, 125400, 5796, 1506960, 300, 93960, 2492028, 505920};
225 while (z.hi < cutoff)
230 P inverse = P(1) / z,
square = inverse * inverse,
c = P(R(nums[terms - 1])) / P(R(dens[terms - 1]));
231 for (
int k = terms - 2; k >= 0; --k)
232 c =
c *
square + P(R(nums[k])) / P(R(dens[k]));
233 return (z - P(R(.5))) * log_pair(z) - z + log_two_pi<R>() / P(2) + inverse *
c -
prefix;
235template <NativeReal R> two_word<R> exp_pair(two_word<R> v)
237 using L = numeric_limits<R>;
238 using P = two_word<R>;
239 if (v.hi < R(2 * (L::min_exponent - L::digits())))
return P(0);
240 P ln2 = log_two<R>();
241 int n = int(std::nearbyint(v.hi / ln2.hi));
242 P r = v - P(R(n)) * ln2, s(1), t(1);
243 for (
int k = 1; k < 2 * L::digits(); ++k)
247 if (std::abs(t.hi) < epsilon<R>() / 16)
break;
251template <NativeReal R> R lower_complement(R av, R xv)
253 using P = two_word<R>;
254 using L = numeric_limits<R>;
255 P a(av), x(xv), g = log_gamma(av),
sum(1), term(1);
256 for (
int k = 1; k <= 128 * L::digits(); ++k)
258 term = term * x / (a + P(R(k)));
260 if (std::abs(term.hi) <= epsilon<R>() * std::abs(
sum.hi) / 16)
262 P lower = exp_pair(a *
log(xv) - x -
log(av) - g) *
sum;
263 return exp(g + log_pair(P(1) - lower));
266 return L::quiet_NaN();
AsyncTask square(ReadBuffer< int > in, WriteBuffer< int > out)
Definition async_tbb_reduction_example.cpp:39
locale_t value
Definition data_table.cpp:49
auto operator-(Async< T > const &x)
Unary negation overload for Async values.
Definition async_ops.hpp:477
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
constexpr detail::scalar_math::exp_fn exp
Natural exponential with scalar-specific precision handling.
Definition math.hpp:465
constexpr detail::scalar_math::upper_gamma_fn upper_gamma
Upper unregularized incomplete Gamma, with shape and lower integration endpoint.
Definition math.hpp:593
constexpr detail::scalar_math::log_fn log
Natural logarithm with scalar-specific precision handling.
Definition math.hpp:467
Definition data_table.cpp:8
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
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
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