11#include <native/attributes.h>
12#include <ftz/config.h>
13#include <ftz/math/exp_coefficients.h>
18#if defined(__x86_64__) || defined(_M_X64)
20#elif defined(__aarch64__) || defined(_M_ARM64)
26namespace ftz::detail::native {
27 using ::native::mask_bits;
28 using fp32x1 = ::native::simd<float,1,::native::scalar>;
29 template <
class V>
concept float_register =
requires {
typename V::bits_type; V::lanes; };
31 template <
class V>
struct fp32_bit_bridge {
32 using bits_type =
typename V::bits_type;
33 static constexpr native_inline bits_type encode(V value)
noexcept {
return value.bits(); }
34 static constexpr native_inline V decode(bits_type value)
noexcept {
return V::from_bits(value); }
36 template <
class V>
struct trig_conversion {
37 using U =
typename V::bits_type;
38 using I =
typename V::template rebind<std::int32_t>;
39 static constexpr native_inline U integer(V value)
noexcept {
40 return U::from_native(__builtin_bit_cast(
typename U::native_type, convert<std::int32_t>(value).to_native()));
42 static constexpr native_inline V floating(U value)
noexcept {
43 return convert<float>(I::from_native(__builtin_bit_cast(
typename I::native_type, value.to_native())));
46 template <
class V>
constexpr native_inline V max(V a,V b)
noexcept {
return select(a>b,a,b); }
52 template <
float_register V>
53 constexpr native_inline V exp_factor(V biased)
noexcept {
55 std::array<float, V::lanes> lanes{};
56 std::array<std::uint32_t, V::lanes> words{};
57 biased.store(lanes.data());
58 for (std::size_t i = 0; i < V::lanes; ++i) {
59 auto const bits = std::bit_cast<std::uint32_t>(lanes[i]);
60 if (bits <= 0x437e0000u) words[i] = std::uint32_t(lanes[i]) << 23;
62 return V::from_bits(V::bits_type::load(words.data()));
64#if defined(__aarch64__) || defined(_M_ARM64)
67 return V::from_bits(::native::fcvtzu(biased).
template left<23>());
68#elif defined(__x86_64__) || defined(_M_X64)
71 if constexpr (V::lanes == 1) {
72 auto const index = _mm_cvttss_si32(_mm_set_ss(biased.to_native()));
73 return V::from_bits(std::uint32_t(index > 0 ? index : 0) << 23);
74 }
else if constexpr (V::lanes == 2 || V::lanes == 3) {
75 return V::from_storage(exp_factor(biased.to_storage()));
76 }
else if constexpr (V::lanes == 4) {
77 auto const index = _mm_max_epi32(_mm_cvttps_epi32(biased.to_native()), _mm_setzero_si128());
78 return V::from_native(_mm_castsi128_ps(_mm_slli_epi32(index, 23)));
79 }
else if constexpr (V::lanes == 8) {
80 auto const index = _mm256_max_epi32(_mm256_cvttps_epi32(biased.to_native()), _mm256_setzero_si256());
81 return V::from_native(_mm256_castsi256_ps(_mm256_slli_epi32(index, 23)));
85 auto const safe = select((biased >= V(0.f)) & (biased <= V(254.f)), biased, V(0.f));
86 return V::from_bits(trig_conversion<V>::integer(safe).
template left<23>());
90 template <
unsigned int Degree = 6,
float_register V, std::
size_t N>
requires (Degree >= 1 && Degree <= 7)
91 native_flatten
constexpr native_inline std::array<V, N> exp_ftz(std::array<V, N>
const & input)
noexcept {
92 if constexpr (N == 0)
return {};
94 auto const & [...x] = input;
95 auto const [...active] = std::array{(!(x < V(-87.33654022216796875f)))...};
99 auto const [...overflow] = std::array{(x > V(88.72283172607421875f))...};
100 auto const [...in_range] = std::array{(active & !overflow)...};
101 auto const [...replacement] = std::array{select(overflow,
102 V(std::bit_cast<float>(0x7f800000u)), V(0.f))...};
103 auto [...r] = std::array{x...};
104 auto const [...n] = std::array{round_even(r * V(1.4426950408889634f))...};
105 ((r =
fma(n, V(-0x1.62e400p-1f), r)), ...);
106 ((r =
fma(n, V(-0x1.7f7d1cp-20f), r)), ...);
107 using C = ::ftz::detail::math::exp_coefficients<Degree>;
108 auto const coefficient = [](
unsigned int word)
noexcept {
return V(std::bit_cast<float>(word)); };
109 auto [...y] = std::array{
fma(r, coefficient(C::leading), coefficient(C::next))...};
110 if constexpr (Degree >= 7) ((y =
fma(r, y, coefficient(C::c5))), ...);
111 if constexpr (Degree >= 6) ((y =
fma(r, y, coefficient(C::c4))), ...);
112 if constexpr (Degree >= 5) ((y =
fma(r, y, coefficient(C::c3))), ...);
113 if constexpr (Degree >= 4) ((y =
fma(r, y, coefficient(C::c2))), ...);
114 if constexpr (Degree >= 3) ((y =
fma(r, y, coefficient(C::c1))), ...);
115 if constexpr (Degree >= 2) ((y =
fma(r, y, coefficient(C::c0))), ...);
116 if constexpr (
requires { masked_scaleb_zero(active...[0], y...[0], n...[0]); }) {
117 return {{masked_scaleb(in_range, replacement, y, n)...}};
122 auto const [...high] = std::array{(n > V(127.f))...};
123 auto const [...factor] = std::array{detail::exp_factor(n + select(high, V(126.f), V(127.f)))...};
124 ((y = y * factor), ...);
125 ((y = select(high, select(high, y, V(0.f)) * V(2.f), y)), ...);
126 return {{select(in_range, y, replacement)...}};
130 template <
unsigned int Degree = 6,
float_register V>
requires (Degree >= 1 && Degree <= 7)
131 constexpr native_inline V exp_ftz(V input)
noexcept {
return exp_ftz<Degree>(std::array{input})[0]; }
136 enum class trig_output { sine, cosine, pair };
138 template <trig_output Output,
bool Hardware,
bool Bounded,
float_register V, std::
size_t N>
139 native_flatten
constexpr native_inline
auto sincos_ftz_kernel(std::array<V, N>
const & input)
noexcept {
140 using B = fp32_bit_bridge<V>;
141 using I =
typename B::bits_type;
142 using C = trig_conversion<V>;
143 if constexpr (N == 0) {
144 if constexpr (Output == trig_output::pair)
return std::pair{std::array<V,0>{}, std::array<V,0>{}};
145 else return std::array<V,0>{};
148 constexpr bool need_sine = Bounded || Output != trig_output::cosine;
149 constexpr bool need_cosine = Bounded || Output != trig_output::sine;
150 auto const constant = [](std::uint32_t word)
noexcept {
return B::decode(I(word)); };
151 auto const & [...original] = input;
153 auto const [...word] = std::array{B::encode(original)...};
154 auto const [...canonical] = std::array{B::decode(word &
155 ((mask_bits<std::uint32_t>((word & I(0x7f800000u)) == I(0)) &
156 I(0x007fffffu)) ^ I(0xffffffffu)))...};
157 auto const [...magnitude] = std::array{B::decode(B::encode(canonical) & I(0x7fffffffu))...};
158 auto [...index] = std::array<I, N>{};
159 auto [...reduced] = std::array{canonical...};
160 if constexpr (Bounded) {
161 auto const [...product] = std::array{(magnitude * constant(0x3fa2f983u))...};
162 ((index = (C::integer(product) + I(1)) & I(0xfffffffeu)), ...);
163 auto const [...multiple] = std::array{C::floating(index)...};
164 ((reduced =
fma(multiple, constant(0xbf490000u), magnitude)), ...);
165 ((reduced =
fma(multiple, constant(0xb97da000u), reduced)), ...);
166 ((reduced =
fma(multiple, constant(0xb3222169u), reduced)), ...);
168 auto const [...active] = std::array{
169 mask_bits<std::uint32_t>((B::encode(reduced) & I(0x7fffffffu)) > I(0x39800000u))...};
170 auto const [...masked] = [&] {
171 if constexpr(Hardware)
return std::array{reduced...};
172 else return std::array{B::decode(B::encode(reduced) & active)...};
174 auto const [...square] = std::array{(masked * masked)...};
175 auto [...sine] = [&] {
176 if constexpr (need_sine)
return std::array{
fma(constant(0xb94ca1f9u), square, constant(0x3c08839eu))...};
177 else return std::array<V,0>{};
179 auto [...cosine] = [&] {
180 if constexpr (need_cosine)
return std::array{
fma(constant(0x37ccf5ceu), square, constant(0xbab6061au))...};
181 else return std::array<V,0>{};
183 if constexpr (need_sine) ((sine =
fma(sine, square, constant(0xbe2aaaa3u))), ...);
184 if constexpr (need_cosine) ((cosine =
fma(cosine, square, constant(0x3d2aaaa5u))), ...);
185 if constexpr (need_sine) ((sine = sine * square), ...);
186 if constexpr (need_cosine) ((cosine = cosine * square), ...);
187 if constexpr (need_cosine) ((cosine = cosine * square), ...);
188 if constexpr (need_cosine) {
189 auto const [...half_square] = std::array{(square * V(0.5f))...};
190 ((cosine = cosine - half_square), ...);
192 if constexpr (need_sine) ((sine =
fma(sine, masked, reduced)), ...);
193 if constexpr (need_cosine) ((cosine = cosine + V(1.0f)), ...);
195 if constexpr (need_sine) ((sine = B::decode((B::encode(sine) & active) |
196 (B::encode(reduced) & (active ^ I(0xffffffffu))))), ...);
197 if constexpr (Bounded) {
198 auto const [...bounded_sine] = [&] {
199 if constexpr (Output != trig_output::cosine)
return std::array{B::decode(select((index & I(2)) == I(0),
200 B::encode(sine), B::encode(cosine)) ^ (B::encode(canonical) & I(0x80000000u)) ^
201 ((index & I(4)).
template left<29>()))...};
202 else return std::array<V,0>{};
204 if constexpr (Output != trig_output::sine)
205 ((cosine = B::decode(select((index & I(2)) == I(0), B::encode(cosine), B::encode(sine)) ^
206 (((index - I(2)) ^ I(0xffffffffu)) & I(4)).
template left<29>())), ...);
207 if constexpr (Output == trig_output::pair)
return std::pair{std::array{bounded_sine...}, std::array{cosine...}};
208 else if constexpr (Output == trig_output::sine)
return std::array{bounded_sine...};
209 else return std::array{cosine...};
211 if constexpr (Output == trig_output::pair)
return std::pair{std::array{sine...}, std::array{cosine...}};
212 else if constexpr (Output == trig_output::sine)
return std::array{sine...};
213 else return std::array{cosine...};
221 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
222 constexpr native_inline std::pair<std::array<V, N>, std::array<V, N>> sincos_reduced_ftz(std::array<V, N>
const & x)
noexcept {
223 return detail::sincos_ftz_kernel<trig_output::pair,Hardware,false>(x);
226 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
227 constexpr native_inline std::pair<std::array<V, N>, std::array<V, N>> sincos_ftz(std::array<V, N>
const & x)
noexcept {
228 return detail::sincos_ftz_kernel<trig_output::pair,Hardware,true>(x);
230 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
231 constexpr native_inline std::array<V, N> sin_reduced_ftz(std::array<V, N>
const & x)
noexcept {
232 return detail::sincos_ftz_kernel<trig_output::sine,Hardware,false>(x);
234 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
235 constexpr native_inline std::array<V, N> cos_reduced_ftz(std::array<V, N>
const & x)
noexcept {
236 return detail::sincos_ftz_kernel<trig_output::cosine,Hardware,false>(x);
238 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
239 constexpr native_inline std::array<V, N> sin_ftz(std::array<V, N>
const & x)
noexcept {
return detail::sincos_ftz_kernel<trig_output::sine,Hardware,true>(x); }
240 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
241 constexpr native_inline std::array<V, N> cos_ftz(std::array<V, N>
const & x)
noexcept {
return detail::sincos_ftz_kernel<trig_output::cosine,Hardware,true>(x); }
242 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline std::pair<V, V> sincos_reduced_ftz(V x)
noexcept {
243 auto [sine, cosine] = sincos_reduced_ftz<Hardware>(std::array{x});
244 return {sine[0], cosine[0]};
246 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline std::pair<V, V> sincos_ftz(V x)
noexcept {
247 auto [sine, cosine] = sincos_ftz<Hardware>(std::array{x});
248 return {sine[0], cosine[0]};
250 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline V sin_reduced_ftz(V x)
noexcept {
return sin_reduced_ftz<Hardware>(std::array{x})[0]; }
251 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline V cos_reduced_ftz(V x)
noexcept {
return cos_reduced_ftz<Hardware>(std::array{x})[0]; }
252 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline V sin_ftz(V x)
noexcept {
return sin_ftz<Hardware>(std::array{x})[0]; }
253 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline V cos_ftz(V x)
noexcept {
return cos_ftz<Hardware>(std::array{x})[0]; }
254 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
255 constexpr native_inline std::pair<float, float> sincos_reduced_ftz(
float x)
noexcept {
256 auto [sine, cosine] = sincos_reduced_ftz<Hardware>(fp32x1(x));
return {sine.value, cosine.value};
258 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
259 constexpr native_inline std::pair<float, float> sincos_ftz(
float x)
noexcept {
260 auto [sine, cosine] = sincos_ftz<Hardware>(fp32x1(x));
return {sine.value, cosine.value};
262 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
263 constexpr native_inline
float sin_reduced_ftz(
float x)
noexcept {
return sin_reduced_ftz<Hardware>(fp32x1(x)).value; }
264 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
265 constexpr native_inline
float cos_reduced_ftz(
float x)
noexcept {
return cos_reduced_ftz<Hardware>(fp32x1(x)).value; }
266 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
267 constexpr native_inline
float sin_ftz(
float x)
noexcept {
return sin_ftz<Hardware>(fp32x1(x)).value; }
268 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
269 constexpr native_inline
float cos_ftz(
float x)
noexcept {
return cos_ftz<Hardware>(fp32x1(x)).value; }
336namespace ftz::detail::native {
337 template <
class F,
class U>
struct expm1_result {
342 template <
bool Hardware,
bool Gain,
float_register V, std::
size_t N>
343 native_flatten
constexpr native_inline
auto expm1_graph(std::array<V, N>
const & input)
noexcept {
344 using B = fp32_bit_bridge<V>;
345 using U =
typename B::bits_type;
346 using result_type = expm1_result<std::array<V, N>, std::array<U, N>>;
347 if constexpr (N == 0)
return result_type{};
349 auto const constant = [](std::uint32_t word)
noexcept {
return B::decode(U(word)); };
350 auto const & [...original] = input;
351 auto [...word] = std::array{B::encode(original)...};
352 U
const finite_limit(0x7f800000u), positive_limit(0x3f800001u), sign(0x80000000u);
353 auto [...valid] = std::array<U, N>{};
355 if constexpr (Gain) {
357 ((valid = mask_bits<std::uint32_t>((finite_limit > word) | (word == sign))), ...);
360 ((valid = mask_bits<std::uint32_t>(
361 (finite_limit > (word ^ sign)) | (positive_limit > word))), ...);
363 ((word = word & valid), ...);
364 ((word = select((word & U(0x7f800000u)) == U(0), word & U(0x80000000u), word)), ...);
365 if constexpr (Gain) ((word = word ^ U(0x80000000u)), ...);
366 auto const [...x] = std::array{max(B::decode(word), V(-18))...};
367 auto const [...n] = std::array{round_even(x * constant(0x3fb8aa3bu))...};
368 auto [...r] = std::array{
fma(n, constant(0xbf317200u), x)...};
369 ((r =
fma(n, constant(0xb5bfbe8eu), r)), ...);
370 ((r = select(n == V(0), x, r)), ...);
372 auto const [...t] = [&] {
373 if constexpr(Hardware)
return std::array{r...};
374 else return std::array{B::decode(select(
375 (B::encode(r) & U(0x7fffffffu)) > U(0x33000000u),
376 B::encode(r), U(0)))...};
378 auto const [...z] = std::array{(t * t)...};
379 auto [...h] = std::array{
fma(t, constant(0x3493f27eu), constant(0x3638ef1du))...};
380 ((h =
fma(t, h, constant(0x37d00d01u))), ...);
381 ((h =
fma(t, h, constant(0x39500d01u))), ...);
382 ((h =
fma(t, h, constant(0x3ab60b61u))), ...);
383 ((h =
fma(t, h, constant(0x3c088889u))), ...);
384 ((h =
fma(t, h, constant(0x3d2aaaabu))), ...);
385 ((h =
fma(t, h, constant(0x3e2aaaabu))), ...);
386 ((h =
fma(t, h, constant(0x3f000000u))), ...);
387 auto [...p] = std::array{
fma(z, h, r)...};
388 if constexpr(Hardware) {
389 ((p = B::decode(select(
390 (B::encode(r) & U(0x7fffffffu)) > U(0x33000000u),
391 B::encode(p), B::encode(r)))), ...);
393 ((p = select(t == V(0), r, p)), ...);
396 auto const [...scale] = std::array{normal_pow2(max(n, V(-24)))...};
397 auto [...result] = std::array{
fma(scale, p, scale - V(1))...};
398 ((result = select(n == V(0), p, result)), ...);
399 ((result = select(n == V(-25), select(p > V(0), constant(0xbf7fffffu), V(-1)), result)), ...);
400 ((result = select(n < V(-25), V(-1), result)), ...);
402 if constexpr(Hardware) {
403 ((word = B::encode(result)), ...);
405 ((word = B::encode(flush_to_zero(result))), ...);
407 if constexpr (Gain) ((word = word ^ U(0x80000000u)), ...);
408 ((word = word & valid), ...);
409 return result_type{{{B::decode(word)...}}, {{(valid & U(1))...}}};
415 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
416 constexpr native_inline
auto expm1_checked(std::array<V, N>
const & input)
noexcept {
417 return detail::expm1_graph<Hardware,false>(input);
420 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V, std::
size_t N>
421 constexpr native_inline
auto damping_gain_checked(std::array<V, N>
const & input)
noexcept {
422 return detail::expm1_graph<Hardware,true>(input);
424 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline
auto expm1_checked(V input)
noexcept {
425 auto r = expm1_checked<Hardware>(std::array{input});
426 return expm1_result<V, typename detail::fp32_bit_bridge<V>::bits_type>{
427 r.value[0], r.valid[0]};
429 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0,
float_register V>
constexpr native_inline
auto damping_gain_checked(V input)
noexcept {
430 auto r = damping_gain_checked<Hardware>(std::array{input});
431 return expm1_result<V, typename detail::fp32_bit_bridge<V>::bits_type>{
432 r.value[0], r.valid[0]};
434 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
435 constexpr native_inline expm1_result<float, std::uint32_t> expm1_checked(
float input)
noexcept {
436 auto r = expm1_checked<Hardware>(fp32x1(input));
437 return {r.value.value, r.valid.value};
439 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
440 constexpr native_inline expm1_result<float, std::uint32_t> damping_gain_checked(
float input)
noexcept {
441 auto r = damping_gain_checked<Hardware>(fp32x1(input));
442 return {r.value.value, r.valid.value};
458 template <
bool Hardware,
float_register V, std::
size_t N>
459 native_flatten
constexpr native_inline std::array<V,N> tanh_ftz(std::array<V,N>
const & input)
noexcept {
460 using B=detail::fp32_bit_bridge<V>;
461 using U=
typename B::bits_type;
462 if constexpr (N==0)
return {};
464 auto const & [...original]=input;
465 auto const [...word]=std::array{B::encode(original)...};
466 auto const [...magnitude]=std::array{(word & U(0x7fffffffu))...};
470 auto const [...safe]=[&] {
471 if constexpr(Hardware)
return std::array{
472 select(magnitude<U(0x41200000u),magnitude,U(0x3f800000u))...};
473 else return std::array{select((magnitude>U(0x39800000u)) &
474 (magnitude<U(0x41200000u)),magnitude,U(0x3f800000u))...};
476 auto const [...x]=std::array{B::decode(safe)...};
477 auto const [...z]=std::array{(x*x)...};
479 auto const [...interval]=std::array{select(safe<=U(0x40800000u),
480 select(safe<=U(0x40000000u),select(safe<=U(0x3f800000u),U(0),U(1)),
481 select(safe<=U(0x40400000u),U(2),U(3))),
482 select(safe<=U(0x41000000u),select(safe<=U(0x40c00000u),U(4),U(5)),U(6)))...};
483 auto coefficient=[](U index,
unsigned c0,
unsigned c1,
unsigned c2,
unsigned c3,
484 unsigned c4,
unsigned c5,
unsigned c6)
noexcept {
485 if constexpr(U::lanes==1) {
486 std::array table{c0,c1,c2,c3,c4,c5,c6,c6};
487 return B::decode(U(table[index.to_native()]));
489#if defined(__x86_64__) || defined(_M_X64)
490 else if constexpr(U::lanes<=8) {
491 auto table=__builtin_bit_cast(__m256i, std::array{c0,c1,c2,c3,c4,c5,c6,c6});
492 if constexpr(U::lanes<=4) {
493 auto i=_mm256_zextsi128_si256(__builtin_bit_cast(__m128i, index.to_native()));
494 auto bits=_mm256_castsi256_si128(_mm256_permutevar8x32_epi32(table,i));
495 return B::decode(U::from_native(__builtin_bit_cast(
typename U::native_type, bits)));
497 auto bits=_mm256_permutevar8x32_epi32(table,__builtin_bit_cast(__m256i, index.to_native()));
498 return B::decode(U::from_native(__builtin_bit_cast(
typename U::native_type, bits)));
501 auto table=__builtin_bit_cast(__m512i, std::array{c0,c1,c2,c3,c4,c5,c6,c6,c0,c1,c2,c3,c4,c5,c6,c6});
502 auto bits=_mm512_permutexvar_epi32(__builtin_bit_cast(__m512i, index.to_native()),table);
503 return B::decode(U::from_native(__builtin_bit_cast(
typename U::native_type, bits)));
505#elif defined(__aarch64__) || defined(_M_ARM64)
507 uint8x16x2_t table{{__builtin_bit_cast(uint8x16_t, std::array{c0,c1,c2,c3}),
508 __builtin_bit_cast(uint8x16_t, std::array{c4,c5,c6,c6})}};
509 auto i=__builtin_bit_cast(uint32x4_t, index.to_native());
510 auto offsets=vaddq_u32(vmulq_n_u32(i,0x04040404u),vdupq_n_u32(0x03020100u));
511 auto bits=vqtbl2q_u8(table,vreinterpretq_u8_u32(offsets));
512 return B::decode(U::from_native(__builtin_bit_cast(
typename U::native_type, bits)));
516 auto const [...t]=std::array{
fma(z,
517 coefficient(interval,0x40000000u,0x3f000000u,0x3e800000u,0x3e800000u,0x3d800000u,0x3d800000u,0x3d000000u),
518 coefficient(interval,0xbf800000u,0xbfa00000u,0xbfd00000u,0xc0480000u,0xbfd00000u,0xc0480000u,0xc0240000u))...};
519 auto [...h]=std::array{coefficient(interval,0x00000000u,0x00000000u,0x00000000u,0x00000000u,0xb9405fbfu,0x00000000u,0x00000000u)...};
520 ((h=
fma(h,t,coefficient(interval,0x00000000u,0x38752140u,0x00000000u,0x00000000u,0x39ab4d5fu,0x00000000u,0x00000000u))),...);
521 ((h=
fma(h,t,coefficient(interval,0x00000000u,0xb9183513u,0xb947bd66u,0xb58f6d10u,0xb9c08feeu,0xb59171b1u,0x00000000u))),...);
522 ((h=
fma(h,t,coefficient(interval,0x34facb37u,0x398d9ee0u,0x39dfe5a4u,0x36863471u,0x3a2bf5fau,0x3671d749u,0x00000000u))),...);
523 ((h=
fma(h,t,coefficient(interval,0xb63a0d2du,0xba2fdf3au,0xba4a3864u,0xb758ec9du,0xbaa65b2au,0xb72757e1u,0xb811c415u))),...);
524 ((h=
fma(h,t,coefficient(interval,0x37813497u,0x3ae1130du,0x3ae2b84bu,0x384b3fb7u,0x3b1584c1u,0x380d4deeu,0x38c8e73cu))),...);
525 ((h=
fma(h,t,coefficient(interval,0xb8bfb3f8u,0xbb8bc302u,0xbb813c08u,0xb9404721u,0xbb86bc79u,0xb8f4646bu,0xb980a2b8u))),...);
526 ((h=
fma(h,t,coefficient(interval,0x3a0e6d24u,0x3c2d6773u,0x3c1129ceu,0x3a356f7bu,0x3bf71905u,0x39d46b54u,0x3a37296bu))),...);
527 ((h=
fma(h,t,coefficient(interval,0xbb535f6cu,0xbcd7a178u,0xbca3c0b8u,0xbb2d3d49u,0xbc67da1fu,0xbabdbc02u,0xbb06690eu))),...);
528 ((h=
fma(h,t,coefficient(interval,0x3c9d20e4u,0x3d86d45du,0x3d3bcdf4u,0x3c2a7e14u,0x3ce35ff2u,0x3bb1ed8au,0x3bcea86au))),...);
529 ((h=
fma(h,t,coefficient(interval,0xbded544du,0xbe2e2df9u,0xbde4f8bcu,0xbd36d397u,0xbd76f5e5u,0xbcb95c19u,0xbcb08499u))),...);
530 ((h=
fma(h,t,coefficient(interval,0x3f5c6e3eu,0x3f14c222u,0x3ec662fcu,0x3e9091d7u,0x3e48ced7u,0x3e10d0b5u,0x3de229ecu))),...);
531 auto [...result]=std::array{B::encode(x*h)...};
532 ((result=select(result>U(0x3f800000u),U(0x3f800000u),result) |
533 (word & U(0x80000000u))),...);
536 ((result=select(magnitude>=U(0x41200000u),
537 (word & U(0x80000000u)) | U(0x3f800000u),result)),...);
538 ((result=select(magnitude<=U(0x39800000u),word,result)),...);
539 ((result=select(magnitude>U(0x7f800000u),U(0x7fc00000u),result)),...);
540 return {{B::decode(result)...}};
550 template <
bool OnePlus,
bool Hardware,
float_register V, std::
size_t N>
551 native_flatten
constexpr native_inline std::array<V,N> log_ftz(std::array<V,N>
const & input)
noexcept {
552 using B=detail::fp32_bit_bridge<V>;
553 using U=
typename B::bits_type;
554 using I=
typename V::template rebind<std::int32_t>;
555 if constexpr (N==0)
return {};
557 auto const & [...original]=input;
558 auto const [...word]=std::array{B::encode(original)...};
559 auto const [...magnitude]=std::array{(word & U(0x7fffffffu))...};
560 auto const [...valid]=[&] {
561 if constexpr (OnePlus)
return std::array{((magnitude < U(0x7f800000u)) &
562 (((word & U(0x80000000u)) == U(0)) | (magnitude < U(0x3f800000u))))...};
563 else return std::array{((word > U(0)) & (word < U(0x7f800000u)))...};
565 auto const [...direct]=[&] {
566 if constexpr (OnePlus)
return std::array{(valid & (magnitude <=
567 select((word & U(0x80000000u)) != U(0),U(0x3f000000u),U(0x3f800000u))))...};
568 else return std::array<typename V::mask,0>{};
570 auto const [...positive]=[&] {
571 if constexpr (OnePlus)
return std::array{B::encode(V(1.0f) +
572 B::decode(select(valid & !direct,word,U(0))))...};
573 else return std::array{select(valid,word,U(0x3f800000u))...};
575 auto [...exponent]=std::array{(positive.template right<23>() - U(127))...};
576 auto [...mantissa]=std::array{((positive & U(0x007fffffu)) | U(0x3f800000u))...};
577 auto const [...upper]=std::array{(mantissa >= U(0x3fc00000u))...};
578 ((mantissa=mantissa-select(upper,U(0x00800000u),U(0))),...);
579 ((exponent=exponent+select(upper,U(1),U(0))),...);
580 auto const [...reduced]=std::array{(B::decode(mantissa)-V(1.0f))...};
581 auto const [...argument]=[&] {
582 if constexpr (OnePlus && Hardware)
return std::array{select(direct,B::decode(word),reduced)...};
583 else if constexpr (OnePlus)
return std::array{select(direct,
584 B::decode(select(magnitude>U(0x33000000u),word,U(0))),reduced)...};
585 else return std::array{reduced...};
587 auto const [...negative]=std::array{((B::encode(argument) & U(0x80000000u)) != U(0))...};
588 auto coefficient=[](
auto mask,
unsigned negative_word,
unsigned positive_word)
noexcept {
589 return B::decode(select(mask,U(negative_word),U(positive_word)));
591 auto const [...square]=std::array{(argument*argument)...};
592 auto const [...t]=std::array{
fma(argument,
593 coefficient(negative,0x40800000u,0x40000000u),
594 coefficient(negative,0x3f800000u,0xbf800000u))...};
595 auto [...h]=std::array{coefficient(negative,0x00000000u,0xb29c7ee2u)...};
596 ((h=
fma(h,t,coefficient(negative,0x00000000u,0x3378ea39u))),...);
597 ((h=
fma(h,t,coefficient(negative,0xb44f5480u,0xb3faaccbu))),...);
598 ((h=
fma(h,t,coefficient(negative,0x352754efu,0x34c9e1cdu))),...);
599 ((h=
fma(h,t,coefficient(negative,0xb5bb75dbu,0xb5b13b5eu))),...);
600 ((h=
fma(h,t,coefficient(negative,0x369a1c19u,0x36902a0au))),...);
601 ((h=
fma(h,t,coefficient(negative,0xb7866f43u,0xb76af011u))),...);
602 ((h=
fma(h,t,coefficient(negative,0x3861235au,0x38423d8au))),...);
603 ((h=
fma(h,t,coefficient(negative,0xb93e98dfu,0xb9225d51u))),...);
604 ((h=
fma(h,t,coefficient(negative,0x3a24a041u,0x3a0988b0u))),...);
605 ((h=
fma(h,t,coefficient(negative,0xbb117f6au,0xbaed1a41u))),...);
606 ((h=
fma(h,t,coefficient(negative,0x3c04b7c5u,0x3bd13ce0u))),...);
607 ((h=
fma(h,t,coefficient(negative,0xbcfda364u,0xbcbeef90u))),...);
608 ((h=
fma(h,t,coefficient(negative,0x3e029133u,0x3db786beu))),...);
609 ((h=
fma(h,t,coefficient(negative,0xbf1a5884u,0xbec19b82u))),...);
610 auto const [...polynomial]=std::array{
fma(square,h,argument)...};
611 auto const [...e]=std::array{convert<float>(I::from_native(
612 __builtin_bit_cast(
typename I::native_type, exponent.to_native())))...};
613 auto const [...low]=std::array{
fma(e,B::decode(U(0x35bfbe8eu)),polynomial)...};
614 auto [...result]=std::array{B::encode(
fma(e,B::decode(U(0x3f317200u)),low))...};
615 if constexpr (OnePlus) {
616 ((result=select(direct,B::encode(polynomial),result)),...);
617 ((result=select(magnitude<=U(0x33000000u),word,result)),...);
618 ((result=select(word==U(0xbf800000u),U(0xff800000u),result)),...);
619 ((result=select(word==U(0x7f800000u),word,result)),...);
620 ((result=select((magnitude>U(0x7f800000u)) |
621 (((word & U(0x80000000u)) != U(0)) & (magnitude>U(0x3f800000u))),U(0x7fc00000u),result)),...);
623 ((result=select(word==U(0x7f800000u),word,result)),...);
624 ((result=select((word & U(0x80000000u)) != U(0),U(0x7fc00000u),result)),...);
625 ((result=select(magnitude==U(0),U(0xff800000u),result)),...);
626 ((result=select(magnitude>U(0x7f800000u),U(0x7fc00000u),result)),...);
628 return {{B::decode(result)...}};
636 template<
bool Hardware,
float_register V,std::
size_t N>
637 native_flatten
constexpr native_inline std::array<V,N> atan2_ftz(
638 std::array<V,N>
const & y_input,std::array<V,N>
const & x_input)
noexcept {
639 using B=detail::fp32_bit_bridge<V>;
640 using U=
typename B::bits_type;
641 using I=
typename V::template rebind<std::int32_t>;
642 if constexpr(N==0)
return {};
644 auto constant=[](
unsigned word)
noexcept {
return B::decode(U(word));};
645 auto integer=[](U word)
noexcept {
646 return I::from_native(__builtin_bit_cast(
typename I::native_type, word.to_native()));
648 auto const & [...y]=y_input;
649 auto const & [...x]=x_input;
650 auto const [...yw]=std::array{B::encode(y)...};
651 auto const [...xw]=std::array{B::encode(x)...};
652 auto const [...ay]=std::array{(yw & U(0x7fffffffu))...};
653 auto const [...ax]=std::array{(xw & U(0x7fffffffu))...};
654 auto const [...swap]=std::array{(ay>ax)...};
655 auto const [...negative_x]=std::array{((xw & U(0x80000000u)) != U(0))...};
656 auto const [...a]=std::array{select(swap,ax,ay)...};
657 auto const [...b]=std::array{select(swap,ay,ax)...};
658 auto const [...ma]=std::array{B::decode((a & U(0x007fffffu)) | U(0x3f800000u))...};
659 auto const [...mb]=std::array{((b & U(0x007fffffu)) | U(0x3f800000u))...};
660 auto const [...m]=std::array{B::decode(mb)...};
661 auto [...r]=std::array{B::decode(U(0x7ef311c3u)-mb)...};
662 auto [...e]=std::array{
fma(-m,r,V(1.0f))...};
663 ((r=
fma(r,e,r)),...);
664 ((e=
fma(-m,r,V(1.0f))),...);
665 ((r=
fma(r,e,r)),...);
666 ((e=
fma(-m,r,V(1.0f))),...);
667 ((r=
fma(r,e,r)),...);
669 auto const [...word]=std::array{B::encode(r)...};
670 auto const [...fraction]=std::array{(word & U(0x007fffffu))...};
671 auto const [...exponent]=std::array{(word.template right<23>()+
672 a.template right<23>()-b.template right<23>())...};
673 auto [...ratio_word]=std::array{select(integer(exponent)>I(0),
674 exponent.template left<23>() | fraction,
675 select((exponent==U(0)) & (fraction==U(0x007fffffu)),U(0x00800000u),U(0)))...};
676 ((ratio_word=select(ratio_word>U(0x3f800000u),U(0x3f800000u),ratio_word)),...);
677 auto const [...tiny]=std::array{(ratio_word<=U(0x39800000u))...};
678 auto const [...ratio]=std::array{B::decode(ratio_word)...};
679 auto const [...t]=[&] {
680 if constexpr(Hardware)
return std::array{ratio...};
681 else return std::array{B::decode(select(tiny,U(0),ratio_word))...};
683 auto const [...z]=std::array{(t*t)...};
684 auto [...h]=std::array{
fma(constant(0x3b390ccdu),z,constant(0xbc82b80du))...};
685 ((h=
fma(h,z,constant(0x3d2e19b6u))),...);
686 ((h=
fma(h,z,constant(0xbd995ffau))),...);
687 ((h=
fma(h,z,constant(0x3dd9ccf2u))),...);
688 ((h=
fma(h,z,constant(0xbe116f9fu))),...);
689 ((h=
fma(h,z,constant(0x3e4cb9a7u))),...);
690 ((h=
fma(h,z,constant(0xbeaaaa5du))),...);
691 auto const [...q]=std::array{(z*h)...};
692 auto [...angle]=std::array{
fma(q,t,ratio)...};
693 ((angle=select(tiny,ratio,angle)),...);
694 ((angle=select(swap,
fma(V(-1.0f),angle,constant(0x3fc90fdbu)),angle)),...);
695 ((angle=select(negative_x,
fma(V(-1.0f),angle,constant(0x40490fdbu)),angle)),...);
696 auto [...result]=std::array{B::encode(angle)...};
697 auto const [...axis]=std::array{select(negative_x,U(0x40490fdbu),U(0))...};
698 ((result=select(ax==U(0),U(0x3fc90fdbu),result)),...);
699 ((result=select(ax==U(0x7f800000u),axis,result)),...);
700 ((result=select(ay==U(0x7f800000u),select(ax==U(0x7f800000u),
701 select(negative_x,U(0x4016cbe4u),U(0x3f490fdbu)),U(0x3fc90fdbu)),result)),...);
702 ((result=select(ay==U(0),axis,result)),...);
703 ((result=result | (yw & U(0x80000000u))),...);
704 ((result=select((ay>U(0x7f800000u)) | (ax>U(0x7f800000u)),U(0x7fc00000u),result)),...);
705 return {{B::decode(result)...}};
constexpr auto fma(A a, B b, C c) noexcept(std::is_nothrow_constructible_v< detail::ftz32_simd< detail::raw_family_t< A, B, C >, detail::scalar_family_t< A, B, C > >, A & > &&std::is_nothrow_constructible_v< detail::ftz32_simd< detail::raw_family_t< A, B, C >, detail::scalar_family_t< A, B, C > >, B & > &&std::is_nothrow_constructible_v< detail::ftz32_simd< detail::raw_family_t< A, B, C >, detail::scalar_family_t< A, B, C > >, C & >)
Imports raw SIMD operands into the FTZ scalar operand's policy and evaluates fused a*b+c....