Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
native_pi_math.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "native_math.hpp"
5
6namespace uni20::detail::native_math
7{
8// The remainder on division by 1/2 is exact for binary inputs. remquo gives
9// enough quotient bits to recover the quadrant, even when 2*x would overflow
10// or when x is far too large for an integer carrier. Only the reduced argument
11// (in [-1/4,1/4]) is multiplied by pi.
12template <NativeReal R> struct portable_pi_math
13{
14 static R sinpi(R x)
15 {
16 if (!std::isfinite(x)) return numeric_limits<R>::quiet_NaN();
17 int q;
18 R r = std::remquo(x, R(0.5), &q);
19 q &= 3;
20 if (r == R(0)) return q % 2 == 0 ? std::copysign(R(0), x) : (q == 1 ? R(1) : R(-1));
21 R angle = pi<R> * r;
22 switch (q)
23 {
24 case 0:
25 return std::sin(angle);
26 case 1:
27 return std::cos(angle);
28 case 2:
29 return -std::sin(angle);
30 default:
31 return -std::cos(angle);
32 }
33 }
34
35 static R cospi(R x)
36 {
37 if (!std::isfinite(x)) return numeric_limits<R>::quiet_NaN();
38 int q;
39 R r = std::remquo(x, R(0.5), &q);
40 q &= 3;
41 if (r == R(0)) return q % 2 != 0 ? R(0) : (q == 0 ? R(1) : R(-1));
42 R angle = pi<R> * r;
43 switch (q)
44 {
45 case 0:
46 return std::cos(angle);
47 case 1:
48 return -std::sin(angle);
49 case 2:
50 return -std::cos(angle);
51 default:
52 return std::sin(angle);
53 }
54 }
55
56 static R tanpi(R x)
57 {
58 if (!std::isfinite(x)) return numeric_limits<R>::quiet_NaN();
59 int q;
60 R r = std::remquo(x, R(0.5), &q);
61 q &= 3;
62 if (r == R(0))
63 {
64 if (q % 2 == 0) return std::copysign(R(0), x);
65 return q == 1 ? numeric_limits<R>::infinity() : -numeric_limits<R>::infinity();
66 }
67 // At a quarter-integer, remquo's ties-to-even quotient is even.
68 // Preserve the exact result independently of libm's tan.
69 if (std::abs(r) == R(0.25)) return std::copysign(R(1), r);
70 R value = std::tan(pi<R> * r);
71 return q % 2 == 0 ? value : -R(1) / value;
72 }
73
74 static R asinpi(R x)
75 {
76 if (x == R(0)) return x;
77 if (std::abs(x) == R(1)) return std::copysign(R(0.5), x);
78 return std::asin(x) / pi<R>;
79 }
80
81 static R acospi(R x)
82 {
83 if (x == R(0)) return R(0.5);
84 if (x == R(1)) return R(0);
85 if (x == R(-1)) return R(1);
86 return std::acos(x) / pi<R>;
87 }
88
89 static R atanpi(R x)
90 {
91 if (x == R(0)) return x;
92 if (std::abs(x) == R(1)) return std::copysign(R(0.25), x);
93 if (std::isinf(x)) return std::copysign(R(0.5), x);
94 return std::atan(x) / pi<R>;
95 }
96
97 static R atan2pi(R y, R x)
98 {
99 if (std::isnan(x) || std::isnan(y)) return numeric_limits<R>::quiet_NaN();
100 if (y == R(0)) return std::copysign(std::signbit(x) ? R(1) : R(0), y);
101 if (x == R(0)) return std::copysign(R(0.5), y);
102 // This also handles all four infinite diagonals, before either infinity
103 // is treated as an axis. No y/x is formed, so extreme ratios remain safe.
104 if (std::abs(x) == std::abs(y)) return std::copysign(x > R(0) ? R(0.25) : R(0.75), y);
105 if (std::isinf(y)) return std::copysign(R(0.5), y);
106 if (std::isinf(x)) return std::copysign(x > R(0) ? R(0) : R(1), y);
107 return std::atan2(y, x) / pi<R>;
108 }
109};
110
111// Each native family is probed for its exact argument/result type and linked
112// symbol. A missing family inherits the portable implementation at that type.
113template <NativeReal R> struct pi_math_provider : portable_pi_math<R>
114{};
115
116#define UNI20_NATIVE_PI_FORWARD(TYPE, SUFFIX) \
117 static TYPE sinpi(TYPE x) { return ::sinpi##SUFFIX(x); } \
118 static TYPE cospi(TYPE x) { return ::cospi##SUFFIX(x); } \
119 static TYPE tanpi(TYPE x) { return ::tanpi##SUFFIX(x); }
120#define UNI20_NATIVE_PI_INVERSE(TYPE, SUFFIX) \
121 static TYPE asinpi(TYPE x) { return ::asinpi##SUFFIX(x); } \
122 static TYPE acospi(TYPE x) { return ::acospi##SUFFIX(x); } \
123 static TYPE atanpi(TYPE x) { return ::atanpi##SUFFIX(x); } \
124 static TYPE atan2pi(TYPE y, TYPE x) { return ::atan2pi##SUFFIX(y, x); }
125
126#if (UNI20_HAS_PI_TRIG_FLOAT || UNI20_HAS_PI_INVERSE_FLOAT)
127template <> struct pi_math_provider<float> : portable_pi_math<float>
128{
129#if UNI20_HAS_PI_TRIG_FLOAT
131#endif
132#if UNI20_HAS_PI_INVERSE_FLOAT
134#endif
135};
136#endif
137
138#if (UNI20_HAS_PI_TRIG_DOUBLE || UNI20_HAS_PI_INVERSE_DOUBLE)
139template <> struct pi_math_provider<double> : portable_pi_math<double>
140{
141#if UNI20_HAS_PI_TRIG_DOUBLE
143#endif
144#if UNI20_HAS_PI_INVERSE_DOUBLE
146#endif
147};
148#endif
149
150#if (UNI20_HAS_PI_TRIG_LONG_DOUBLE || UNI20_HAS_PI_INVERSE_LONG_DOUBLE)
151template <> struct pi_math_provider<long double> : portable_pi_math<long double>
152{
153#if UNI20_HAS_PI_TRIG_LONG_DOUBLE
154 UNI20_NATIVE_PI_FORWARD(long double, l)
155#endif
156#if UNI20_HAS_PI_INVERSE_LONG_DOUBLE
157 UNI20_NATIVE_PI_INVERSE(long double, l)
158#endif
159};
160#endif
161
162#if UNI20_HAS_FLOAT128 && (UNI20_HAS_PI_TRIG_FLOAT128 || UNI20_HAS_PI_INVERSE_FLOAT128)
163template <> struct pi_math_provider<float128> : portable_pi_math<float128>
164{
165#if UNI20_HAS_PI_TRIG_FLOAT128
166 UNI20_NATIVE_PI_FORWARD(float128, f128)
167#endif
168#if UNI20_HAS_PI_INVERSE_FLOAT128
169 UNI20_NATIVE_PI_INVERSE(float128, f128)
170#endif
171};
172#endif
173
174#undef UNI20_NATIVE_PI_FORWARD
175#undef UNI20_NATIVE_PI_INVERSE
176
177template <NativeReal R> R sinpi(R x) { return pi_math_provider<R>::sinpi(x); }
178template <NativeReal R> R cospi(R x) { return pi_math_provider<R>::cospi(x); }
179template <NativeReal R> R tanpi(R x) { return pi_math_provider<R>::tanpi(x); }
180template <NativeReal R> R asinpi(R x) { return pi_math_provider<R>::asinpi(x); }
181template <NativeReal R> R acospi(R x) { return pi_math_provider<R>::acospi(x); }
182template <NativeReal R> R atanpi(R x) { return pi_math_provider<R>::atanpi(x); }
183template <NativeReal R, NativeReal S> auto atan2pi(R y, S x)
184{
185 using T = std::common_type_t<R, S>;
186 return pi_math_provider<T>::atan2pi(T(y), T(x));
187}
188} // namespace uni20::detail::native_math
locale_t value
Definition data_table.cpp:49
constexpr detail::scalar_math::asinpi_fn asinpi
Principal arcsine divided by pi.
Definition math.hpp:646
constexpr detail::scalar_math::atanpi_fn atanpi
Principal arctangent divided by pi.
Definition math.hpp:650
constexpr detail::scalar_math::sinpi_fn sinpi
Sine of pi times x, with argument reduction before multiplication by pi.
Definition math.hpp:640
constexpr detail::scalar_math::atan2pi_fn atan2pi
Quadrant-aware arctangent divided by pi.
Definition math.hpp:664
constexpr detail::scalar_math::acospi_fn acospi
Principal arccosine divided by pi.
Definition math.hpp:648
constexpr detail::scalar_math::tanpi_fn tanpi
Tangent of pi times x, with argument reduction before multiplication by pi.
Definition math.hpp:644
constexpr detail::scalar_math::cospi_fn cospi
Cosine of pi times x, with argument reduction before multiplication by pi.
Definition math.hpp:642
#define UNI20_NATIVE_PI_FORWARD(TYPE, SUFFIX)
Definition native_pi_math.hpp:116
#define UNI20_NATIVE_PI_INVERSE(TYPE, SUFFIX)
Definition native_pi_math.hpp:120