Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
native_special_math.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "native_math.hpp"
6
7namespace uni20::detail::native_math
8{
9template <class R> struct standard_special_provider
10{};
11
12// The standard feature macro guarantees float, double and long double support.
13// Extension types need their own provider: a viable std template declaration
14// alone does not establish a working, precision-preserving implementation.
15// Some libstdc++/glibc providers write signgam internally in beta.
16// Their results do not use that sign, but concurrent calls retain an upstream
17// data race. See docs/tensor/native_special_functions.md for the accepted
18// provider limitation and the restriction on application use of signgam.
19#if defined(__cpp_lib_math_special_functions) && __cpp_lib_math_special_functions >= 201603L
20template <class R>
21 requires(std::same_as<R, float> || std::same_as<R, double> || std::same_as<R, long double>)
22struct standard_special_provider<R>
23{
24 static R beta(R x, R y) { return std::beta(x, y); }
25};
26#endif
27
28template <NativeReal R> R beta(R x, R y)
29{
30 if constexpr (requires { standard_special_provider<R>::beta(x, y); })
31 return standard_special_provider<R>::beta(x, y);
32 else
33 return native_math::portable_beta(x, y);
34}
35template <class R> struct lgamma_provider
36{};
37template <class R> struct bessel_provider
38{};
39
40#define UNI20_NATIVE_LGAMMA_PROVIDER(TYPE, FUNCTION) \
41 template <> struct lgamma_provider<TYPE> \
42 { \
43 static TYPE call(TYPE x, int* sign) { return FUNCTION(x, sign); } \
44 };
45#if UNI20_HAS_LGAMMA_R_FLOAT
46UNI20_NATIVE_LGAMMA_PROVIDER(float, ::lgammaf_r)
47#endif
48#if UNI20_HAS_LGAMMA_R_DOUBLE
49UNI20_NATIVE_LGAMMA_PROVIDER(double, ::lgamma_r)
50#endif
51#if UNI20_HAS_LGAMMA_R_LONG_DOUBLE
52UNI20_NATIVE_LGAMMA_PROVIDER(long double, ::lgammal_r)
53#endif
54#if UNI20_HAS_FLOAT128 && UNI20_HAS_LGAMMA_R_FLOAT128
55UNI20_NATIVE_LGAMMA_PROVIDER(uni20::float128, ::lgammaf128_r)
56#endif
57#undef UNI20_NATIVE_LGAMMA_PROVIDER
58
59#define UNI20_NATIVE_BESSEL_PROVIDER(TYPE, J, Y) \
60 template <> struct bessel_provider<TYPE> \
61 { \
62 static TYPE first(int n, TYPE x) { return J(n, x); } \
63 static TYPE second(int n, TYPE x) { return Y(n, x); } \
64 };
65#if UNI20_HAS_BESSEL_FLOAT
66UNI20_NATIVE_BESSEL_PROVIDER(float, ::jnf, ::ynf)
67#endif
68#if UNI20_HAS_BESSEL_DOUBLE
69UNI20_NATIVE_BESSEL_PROVIDER(double, ::jn, ::yn)
70#endif
71#if UNI20_HAS_BESSEL_LONG_DOUBLE
72UNI20_NATIVE_BESSEL_PROVIDER(long double, ::jnl, ::ynl)
73#endif
74#if UNI20_HAS_FLOAT128 && UNI20_HAS_BESSEL_FLOAT128
75UNI20_NATIVE_BESSEL_PROVIDER(uni20::float128, ::jnf128, ::ynf128)
76#endif
77#undef UNI20_NATIVE_BESSEL_PROVIDER
78
79template <NativeReal R>
80 requires requires(R x, int* sign) { lgamma_provider<R>::call(x, sign); }
81lgamma_result<R> lgamma_sign(R x)
82{
83 int sign = 0;
84 R value = lgamma_provider<R>::call(x, &sign);
85 // The sign at a Gamma pole is undefined; normalize provider conventions to
86 // the shared result contract. Signed zero retains its one-sided pole sign.
87 if (std::isnan(x) || (x < R(0) && (std::isinf(x) || std::floor(x) == x)))
88 sign = 0;
89 else if (x == R(0))
90 sign = std::signbit(x) ? -1 : 1;
91 return {value, sign};
92}
93
94template <NativeReal R>
95 requires requires(R x) { native_math::lgamma_sign(x); }
96R lgamma(R x)
97{
98 return native_math::lgamma_sign(x).value;
99}
100
101template <MathInteger I> int native_bessel_order(I value)
102{
103 auto n = math_integer(value);
104 // Normalize negative orders before calling providers, which may negate an
105 // int internally. In particular INT_MIN cannot be lowered safely.
106 constexpr auto maximum = numeric_limits<int>::max();
107 if (n < -std::int64_t(maximum) || n > maximum)
108 throw std::out_of_range("native Bessel absolute order does not fit int");
109 return static_cast<int>(n);
110}
111
112template <MathInteger I, NativeReal R>
113 requires requires(R x) { bessel_provider<R>::first(0, x); }
114R bessel_j(I order, R x)
115{
116 int n = native_bessel_order(order);
117 R result = bessel_provider<R>::first(n < 0 ? -n : n, x);
118 return n < 0 && n % 2 != 0 ? -result : result;
119}
120
121template <MathInteger I, NativeReal R>
122 requires requires(R x) { bessel_provider<R>::second(0, x); }
123R bessel_y(I order, R x)
124{
125 int n = native_bessel_order(order);
126 R result = bessel_provider<R>::second(n < 0 ? -n : n, x);
127 return n < 0 && n % 2 != 0 ? -result : result;
128}
129} // namespace uni20::detail::native_math
locale_t value
Definition data_table.cpp:49
constexpr detail::scalar_math::lgamma_sign_fn lgamma_sign
Log-absolute-Gamma and its sign as owning values.
Definition math.hpp:572
constexpr detail::scalar_math::bessel_j_fn bessel_j
Integer-order Bessel function of the first kind, with order first.
Definition math.hpp:613
constexpr detail::scalar_math::lgamma_fn lgamma
Logarithm of the absolute Gamma function; native providers are reentrant.
Definition math.hpp:569
constexpr detail::scalar_math::bessel_y_fn bessel_y
Integer-order Bessel function of the second kind, with order first.
Definition math.hpp:617
constexpr detail::scalar_math::beta_fn beta
Real Beta function.
Definition math.hpp:586
#define UNI20_NATIVE_LGAMMA_PROVIDER(TYPE, FUNCTION)
Definition native_special_math.hpp:40
#define UNI20_NATIVE_BESSEL_PROVIDER(TYPE, J, Y)
Definition native_special_math.hpp:59