4#include "native/simd.h"
5#include "native/wide_pack.h"
12 template<
class T, std::
size_t K, isa A>
13 inline constexpr bool math_vector_shape = [] {
14 if constexpr (!std::same_as<T, float> || K <= 1 || !A.valid())
return false;
15 else if constexpr (A.family ==
x86)
16 return A.has(avx2) && ((K >= 2 && K <= 4) || K == 8 ||
17 (K == 16 && A.has(kernel_base)));
18 else if constexpr (A.family == arm)
19 return A.has(neon) && K >= 2 && K <= 4;
20 else if constexpr (A.family == wasm)
21 return A.has(wasm_feature::simd128) && K == 4;
26 template<
class T, std::
size_t K, isa A>
27 inline constexpr std::size_t math_polynomial_width =
28 !math_vector_shape<T, K, A> ? 1 : K >= 4 && A.family != wasm ? 4 : 2;
30 template<
class T, std::
size_t K, isa A>
31 inline constexpr std::size_t math_branching_width = math_vector_shape<T, K, A> ? 2 : 1;
37 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
39 if constexpr (!detail::math_vector_shape<T, K, A>)
return std::size_t{1};
40 else if constexpr (A.family ==
wasm)
return std::size_t{2};
41 else if constexpr (A.family ==
x86 && K >= 8)
return std::size_t{6};
42 else return std::size_t{4};
46 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
47 inline constexpr std::size_t
exp2_width = detail::math_branching_width<T, K, A>;
50 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
51 inline constexpr std::size_t
expm1_width = detail::math_polynomial_width<T, K, A>;
54 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
58 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
59 inline constexpr std::size_t
log_width = detail::math_polynomial_width<T, K, A>;
62 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
66 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
67 inline constexpr std::size_t
log1p_width = detail::math_polynomial_width<T, K, A>;
70 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
71 inline constexpr std::size_t
tanh_width = detail::math_branching_width<T, K, A>;
75 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
76 inline constexpr std::size_t
atan2_width = detail::math_branching_width<T, K, A>;
79 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
81 detail::math_vector_shape<T, K, A> && A.family ==
arm && K == 4
82 ? 4 : detail::math_branching_width<T, K, A>;
85 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
89 template<
class T, std::
size_t K, isa A = NATIVE_BA
SELINE>
requires (K > 0)
108namespace wide::detail {
109 template<
class V>
struct native_ops;
111#define NATIVE_BACKEND_BODY "native/wide_native_body.h"
112#include "native/simd/for_each_backend.h"
113#undef NATIVE_BACKEND_BODY
115namespace wide::detail {
116 template<
class P>
inline constexpr bool native_pack =
false;
117 template<
class V, std::
size_t N>
118 inline constexpr bool native_pack<std::array<V, N>> =
requires {
sizeof(native_ops<V>); };
119 template<
class P>
concept native_pack_argument = native_pack<std::remove_cvref_t<P>>;
120 template<
class V>
concept native_leaf_argument = (!pack<V>) &&
121 requires {
sizeof(native_ops<std::remove_cvref_t<V>>); };
125 template<
class A>
struct lift_argument {
using type=std::remove_cvref_t<A>; };
126 template<
class V,std::
size_t N>
struct lift_argument<std::array<V,N>> {
using type=V; };
127 template<
class A>
using lift_argument_t=
typename lift_argument<std::remove_cvref_t<A>>::type;
128 template<
class A,
class... Rest>
consteval std::size_t lift_size() {
129 if constexpr (pack<A>)
return shape_t<A>::size;
130 else return lift_size<Rest...>();
132 template<std::
size_t N,
class A>
consteval bool lift_size_matches() {
133 if constexpr (pack<A>)
return shape_t<A>::size==N;
136 template<
class F,
class... A>
consteval bool lift_result_valid() {
137 if constexpr (!std::is_invocable_v<F const &,lift_argument_t<A>
const &...>)
return false;
139 using R=std::invoke_result_t<F const &,lift_argument_t<A>
const &...>;
140 return std::is_object_v<std::remove_cvref_t<R>> &&
141 std::is_constructible_v<std::remove_cvref_t<R>,R>;
144 template<
class F,
class... A>
concept liftable = (pack<A> || ...) &&
145 ((native_pack_argument<A> || native_leaf_argument<A>) && ...) &&
146 (lift_size_matches<lift_size<A...>(),A>() && ...) && lift_result_valid<F,A...>();
147 template<std::
size_t I,
class A>
148 native_inline constexpr decltype(
auto) lift_operand(A
const & value) {
149 if constexpr (pack<A>)
return std::get<I>(value);
152 template<std::size_t I,
class F,
class... A>
153 native_inline constexpr auto lift_element(F
const & function,A
const &... arguments) {
154 return function(lift_operand<I>(arguments)...);
156 template<
class F,
class... A,std::size_t... I>
157 native_inline constexpr auto lift_array(F
const & function,std::index_sequence<I...>,A
const &... arguments) {
158 using R=std::remove_cvref_t<std::invoke_result_t<F const &,lift_argument_t<A>
const &...>>;
159 return std::array<R,
sizeof...(I)>{{lift_element<I>(function,arguments...)...}};
161 template<
class F,
class... A>
requires liftable<F,A...>
162 native_inline constexpr auto lift(F function,A
const &... arguments) {
163 return lift_array(function,std::make_index_sequence<lift_size<A...>()>{},arguments...);
166#define NATIVE_WIDE_BINARY_OPERATION(name,bridge,op) \
168 template<class V> requires requires(V a) { a op a; } \
169 native_inline constexpr auto operator()(V const & a,V const & b) const { return native_ops<V>::bridge(a,b); } \
171 NATIVE_WIDE_BINARY_OPERATION(add,add,+)
172 NATIVE_WIDE_BINARY_OPERATION(subtract,sub,-)
173 NATIVE_WIDE_BINARY_OPERATION(multiply,mul,*)
174 NATIVE_WIDE_BINARY_OPERATION(divide,div,/)
175 NATIVE_WIDE_BINARY_OPERATION(bit_and,bit_and,&)
176 NATIVE_WIDE_BINARY_OPERATION(bit_or,bit_or,|)
177 NATIVE_WIDE_BINARY_OPERATION(bit_xor,bit_xor,^)
178 NATIVE_WIDE_BINARY_OPERATION(equal,equal,==)
179 NATIVE_WIDE_BINARY_OPERATION(unequal,unequal,!=)
180 NATIVE_WIDE_BINARY_OPERATION(less,less,<)
181 NATIVE_WIDE_BINARY_OPERATION(less_equal,less_equal,<=)
182 NATIVE_WIDE_BINARY_OPERATION(greater,greater,>)
183 NATIVE_WIDE_BINARY_OPERATION(greater_equal,greater_equal,>=)
184#undef NATIVE_WIDE_BINARY_OPERATION
185#define NATIVE_WIDE_UNARY_OPERATION(name,bridge,op) \
187 template<class V> requires requires(V a) { { op a } -> std::same_as<V>; } \
188 native_inline constexpr auto operator()(V const & a) const { return native_ops<V>::bridge(a); } \
190 NATIVE_WIDE_UNARY_OPERATION(negate,negate,-)
191 NATIVE_WIDE_UNARY_OPERATION(bit_not,bit_not,~)
192 NATIVE_WIDE_UNARY_OPERATION(logical_not,logical_not,!)
193#undef NATIVE_WIDE_UNARY_OPERATION
194#define NATIVE_WIDE_UNARY_MATH(name,bridge,operation) \
196 template<class V> requires requires(V a) { { operation(a) } -> std::same_as<V>; } \
197 native_inline constexpr auto operator()(V const & a) const { return native_ops<V>::bridge(a); } \
199 NATIVE_WIDE_UNARY_MATH(absolute,absolute,abs)
200 NATIVE_WIDE_UNARY_MATH(root,root,sqrt)
201 NATIVE_WIDE_UNARY_MATH(downward,downward,floor)
202 NATIVE_WIDE_UNARY_MATH(upward,upward,ceil)
203 NATIVE_WIDE_UNARY_MATH(truncate,truncate,trunc)
204 NATIVE_WIDE_UNARY_MATH(round,round,round_even)
205#undef NATIVE_WIDE_UNARY_MATH
207 template<
class V>
requires requires(V a) { select(a<a,a,a); }
208 native_inline constexpr auto operator()(V
const & a,V
const & b)
const {
return native_ops<V>::minimum(a,b); }
211 template<
class V>
requires requires(V a) { select(a>a,a,a); }
212 native_inline constexpr auto operator()(V
const & a,V
const & b)
const {
return native_ops<V>::maximum(a,b); }
215 template<
class V>
requires requires(V a) {
fma(a,a,a); }
216 native_inline constexpr auto operator()(V
const & a,V
const & b,V
const & c)
const {
return native_ops<V>::fused(a,b,c); }
218 struct polynomial_madd {
220 native_inline constexpr auto operator()(V
const & a,V
const & b,V
const & c)
const {
221 return native_ops<V>::polynomial_madd(a,b,c);
224 struct exp_power_operation {
226 native_inline constexpr auto operator()(V
const & n)
const {
return native_ops<V>::exp_power(n); }
228 template<
class P>
requires liftable<exp_power_operation,P>
229 native_inline constexpr auto exp_power(P
const & n)
noexcept {
return lift(exp_power_operation{},n); }
231 template<
class M,
class V>
232 native_inline constexpr auto operator()(M
const & m,V
const & replacement,V
const & a,V
const & n)
const {
233 return native_ops<V>::exp_scale(m,replacement,a,n);
236 template<
class P,
class Q,
class R>
requires liftable<polynomial_madd,P,Q,R>
237 native_inline constexpr auto madd(P
const & a,Q
const & b,R
const & c)
noexcept {
238 return lift(polynomial_madd{},a,b,c);
240 template<
class M,
class P,
class Q>
requires liftable<exp_scale,M,P,P,Q>
241 native_inline constexpr auto scale_exp(M
const & in_range,P
const & replacement,P
const & y,Q
const & n)
noexcept {
242 return lift(exp_scale{},in_range,replacement,y,n);
246 native_inline constexpr auto operator()(M
const & m,V
const & a,V
const & n)
const {
return native_ops<V>::scale(m,a,n); }
249 template<
class V>
requires requires(V a) {
scaleb(a,a); }
250 native_inline constexpr auto operator()(V
const & a,V
const & n)
const {
return native_ops<V>::scale_all(a,n); }
253 template<
class M,
class V>
requires requires(M m,V a) { select(m,a,a); }
254 native_inline constexpr auto operator()(M
const & m,V
const & a,V
const & b)
const {
return native_ops<V>::choose(m,a,b); }
257 template<
class M,
class V>
requires requires(M m,V a) {
masked_scaleb(m,a,a,a); }
258 native_inline constexpr auto operator()(M
const & m,V
const & prior,V
const & a,V
const & n)
const {
259 return native_ops<V>::scale_merge(m,prior,a,n);
263 template<
class V>
requires std::same_as<typename V::value_type,float> &&
requires(V a) { a.bits(); }
264 native_inline constexpr auto operator()(V
const & a)
const {
return native_ops<V>::encode(a); }
267 template<
class V>
requires std::same_as<typename V::value_type,std::uint32_t> &&
268 requires(V a) { V::template rebind<float>::from_bits(a); }
269 native_inline constexpr auto operator()(V
const & a)
const {
return native_ops<V>::decode(a); }
271 template<
unsigned Shift>
struct shift_left {
272 template<
class V>
requires requires(V a) { a.template left<Shift>(); }
273 native_inline constexpr auto operator()(V
const & a)
const {
return native_ops<V>::template left<Shift>(a); }
275 template<
unsigned Shift>
struct shift_right {
276 template<
class V>
requires requires(V a) { a.template right<Shift>(); }
277 native_inline constexpr auto operator()(V
const & a)
const {
return native_ops<V>::template right<Shift>(a); }
279 template<
class T>
struct mask_words {
281 native_inline constexpr auto operator()(V
const & a)
const {
return native_ops<V>::template mask_words<T>(a); }
283 struct trig_integer_operation {
284 template<
class V>
requires std::same_as<typename V::value_type,float>
285 native_inline constexpr auto operator()(V
const & a)
const {
return native_ops<V>::trig_integer(a); }
287 struct tanh_coefficient {
288 std::array<std::uint32_t,8> table;
289 template<
class V>
requires std::same_as<typename V::value_type,std::uint32_t>
290 native_inline constexpr auto operator()(V
const & index)
const noexcept {
291 return native_ops<V>::tanh_coefficient(index,table);
294 struct signed_float_operation {
295 template<
class V>
requires std::same_as<typename V::value_type,std::uint32_t>
296 native_inline constexpr auto operator()(V
const & a)
const {
return native_ops<V>::signed_float(a); }
298 template<
class P>
requires liftable<trig_integer_operation,P>
299 native_inline constexpr auto trig_integer(P
const & a)
noexcept {
return lift(trig_integer_operation{},a); }
300 template<
class P>
requires liftable<signed_float_operation,P>
301 native_inline constexpr auto signed_float(P
const & a)
noexcept {
return lift(signed_float_operation{},a); }
307 template<
class V,std::
size_t N,
class T>
308 requires detail::native_leaf_argument<V> &&
309 (std::same_as<T,float> || std::same_as<T,std::uint32_t>) && std::same_as<typename V::value_type,T>
311 return detail::native_ops<V>::constant(value);
313#define NATIVE_WIDE_BINARY_API(name,operation) \
314 template<class P,class Q> requires detail::liftable<detail::operation,P,Q> \
315 native_nodiscard native_inline constexpr auto name(P const & a,Q const & b) noexcept { \
316 return detail::lift(detail::operation{},a,b); \
318 NATIVE_WIDE_BINARY_API(add,add)
319 NATIVE_WIDE_BINARY_API(sub,subtract)
320 NATIVE_WIDE_BINARY_API(mul,multiply)
321 NATIVE_WIDE_BINARY_API(div,divide)
322 NATIVE_WIDE_BINARY_API(bit_and,bit_and)
323 NATIVE_WIDE_BINARY_API(bit_or,bit_or)
324 NATIVE_WIDE_BINARY_API(bit_xor,bit_xor)
325 NATIVE_WIDE_BINARY_API(cmp_eq,equal)
326 NATIVE_WIDE_BINARY_API(cmp_ne,unequal)
327 NATIVE_WIDE_BINARY_API(cmp_lt,less)
328 NATIVE_WIDE_BINARY_API(cmp_le,less_equal)
329 NATIVE_WIDE_BINARY_API(cmp_gt,greater)
330 NATIVE_WIDE_BINARY_API(cmp_ge,greater_equal)
331 NATIVE_WIDE_BINARY_API(min,minimum)
332 NATIVE_WIDE_BINARY_API(max,maximum)
333 NATIVE_WIDE_BINARY_API(scaleb,scale_all)
334#undef NATIVE_WIDE_BINARY_API
335#define NATIVE_WIDE_UNARY_API(name,operation) \
336 template<class P> requires detail::liftable<detail::operation,P> \
337 native_nodiscard native_inline constexpr auto name(P const & a) noexcept { \
338 return detail::lift(detail::operation{},a); \
340 NATIVE_WIDE_UNARY_API(negate,negate)
341 NATIVE_WIDE_UNARY_API(bit_not,bit_not)
342 NATIVE_WIDE_UNARY_API(mask_not,logical_not)
343 NATIVE_WIDE_UNARY_API(abs,absolute)
344 NATIVE_WIDE_UNARY_API(sqrt,root)
345 NATIVE_WIDE_UNARY_API(floor,downward)
346 NATIVE_WIDE_UNARY_API(ceil,upward)
347 NATIVE_WIDE_UNARY_API(trunc,truncate)
348 NATIVE_WIDE_UNARY_API(round_even,round)
349 NATIVE_WIDE_UNARY_API(bits,encode)
350 NATIVE_WIDE_UNARY_API(from_bits,decode)
351#undef NATIVE_WIDE_UNARY_API
354 template<
class P,
class Q,
class R>
requires detail::liftable<detail::fused,P,Q,R>
356 return detail::lift(detail::fused{},a,b,c);
360 template<
class P,
class Q,
class R>
requires detail::liftable<detail::scale,P,Q,R>
362 return detail::lift(detail::scale{},m,a,n);
366 template<
class M,
class P,
class Q>
requires detail::liftable<detail::choose,M,P,Q>
368 return detail::lift(detail::choose{},m,a,b);
372 template<
class M,
class P,
class Q,
class R>
requires detail::liftable<detail::scale_merge,M,P,Q,R>
374 return detail::lift(detail::scale_merge{},m,prior,a,n);
378 template<
unsigned Shift,
class P>
requires detail::liftable<detail::shift_left<Shift>,P>
380 return detail::lift(detail::shift_left<Shift>{},a);
383 template<
unsigned Shift,
class P>
requires detail::liftable<detail::shift_right<Shift>,P>
385 return detail::lift(detail::shift_right<Shift>{},a);
389 template<
class T,
class P>
requires detail::liftable<detail::mask_words<T>,P>
391 return detail::lift(detail::mask_words<T>{},a);
395 template<
class T>
inline constexpr bool binary32_register =
false;
396 template<std::
size_t N, ::native::isa<> A>
397 inline constexpr bool binary32_register<::native::simd<float, N, A>> =
398 requires(::native::simd<float,N,A> a) { { a+a } -> std::same_as<::native::simd<float,N,A>>; };
399 template<
class P>
inline constexpr bool binary32_array =
false;
400 template<
class V, std::
size_t N>
401 inline constexpr bool binary32_array<std::array<V, N>> = binary32_register<V>;
402 template<
class P>
inline constexpr bool binary32_pack = binary32_array<P>;
408 template<
class T>
using horner_register_t = typename ::wide::canonical_t<T>::value_type;
409 template<
class T,
class C>
concept horner_coefficient = std::same_as<C, float> ||
410 std::same_as<C, horner_register_t<T>> ||
411 (::wide::detail::is_array<T> && ::wide::detail::is_array<C> &&
412 ::wide::detail::shape_t<T>::size == ::wide::detail::shape_t<C>::size &&
413 (std::same_as<typename ::wide::detail::shape_t<C>::template element_type<0>,
float> ||
414 std::same_as<typename ::wide::detail::shape_t<C>::template element_type<0>, horner_register_t<T>>));
416 template<
class P,
class C0,
class C1,
class... C>
417 native_inline constexpr auto horner_kernel(P
const & z, C0
const & first,
418 C1
const & second, C
const &... rest)
noexcept {
419 auto h = ::wide::detail::madd(first, z, second);
420 ((h = ::wide::detail::madd(h, z, rest)), ...);
426 template<
class P>
struct horner_coefficient_conversion {
430 native_inline constexpr auto operator()(C
const & value)
const noexcept {
431 using shape = ::wide::detail::shape_t<C>;
432 if constexpr (shape::kind == ::wide::detail::family::legacy)
433 return ::wide::map(*
this, value.registers);
434 else if constexpr (shape::kind == ::wide::detail::family::std_array)
435 return ::wide::map(*
this, value);
436 else if constexpr (std::same_as<C, float>)
437 return ::wide::constant_like(input, value);
442 template<
class T,
class C0,
class... C>
444 C0
const & first, C
const &... rest)
noexcept {
445 auto const input = ::wide::promote(z);
446 auto const coefficient = horner_coefficient_conversion<
decltype(input)>{input};
447 if constexpr (
sizeof...(C) == 0) {
448 auto const value = coefficient(first);
449 if constexpr (::wide::pack<
decltype(value)>) return ::wide::demote<T>(value);
450 else return ::wide::demote<T>(::wide::map([&](
auto const &) {
return value; }, input));
452 return ::wide::demote<T>(horner_kernel(input,
453 coefficient(first), coefficient(rest)...));
457 template<
class... C>
struct horner_polynomial {
458 std::tuple<C...> coefficients;
460 template<::w
ide::promotable T>
461 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>) &&
462 (horner_coefficient<T, C> && ...)
464 auto const & [...values] = coefficients;
465 return evaluate_horner(z, values...);
478 template<::wide::promotable C0, ::wide::promotable... C>
479 requires (::wide::detail::binary32_array<::wide::canonical_t<C0>>) &&
480 ((::wide::detail::binary32_array<::wide::canonical_t<C>>) && ...)
482 return detail::horner_polynomial<C0, C...>{{first, rest...}};
488 template<
unsigned Degree,
class V, std::
size_t N>
489 requires (Degree >= 1 && Degree <= 7) && (::wide::detail::binary32_register<V>)
490 native_inline constexpr auto exp_polynomial(std::array<V, N>
const & r)
noexcept {
491 auto const c = [&](
float value) { return ::wide::constant_like(r, value); };
492 if constexpr (Degree == 1)
493 return ::math::horner(c(0x1.ec7054p-1f), c(1.f))(r);
494 else if constexpr (Degree == 2)
495 return ::math::horner(c(0x1.ff3a12p-2f), c(0x1.039e16p+0f), c(1.f))(r);
496 else if constexpr (Degree == 3)
497 return ::math::horner(c(0x1.5249a2p-3f), c(0x1.021d64p-1f),
498 c(0x1.000cd6p+0f), c(1.f))(r);
499 else if constexpr (Degree == 4)
500 return ::math::horner(c(0x1.541326p-5f), c(0x1.57ce98p-3f),
501 c(0x1.0003f6p-1f), c(0x1.fffba8p-1f), c(1.f))(r);
502 else if constexpr (Degree == 5)
503 return ::math::horner(c(0x1.0f9fa4p-7f), c(0x1.573a1cp-5f),
504 c(0x1.555a8p-3f), c(0x1.fffdc6p-2f), c(0x1.fffff6p-1f), c(1.f))(r);
505 else if constexpr (Degree == 6)
506 return ::math::horner(c(0x1.6d55f4p-10f), c(0x1.123e2cp-7f),
507 c(0x1.5554ep-5f), c(0x1.55548ap-3f), c(0.5f), c(1.f), c(1.f))(r);
509 return ::math::horner(c(0x1.a1d714d7b1510dp-13f), c(0x1.6da756e670ea6p-10f),
510 c(0x1.11105b3161a6fp-7f), c(0x1.5554649b7487fp-5f),
511 c(0x1.555555c673724p-3f), c(0x1.0000005c8dd89p-1f), c(1.f), c(1.f))(r);
515 template<
bool Flush,
unsigned Degree = 6,
class V, std::
size_t N>
516 requires (Degree >= 1 && Degree <= 7) && (::wide::detail::binary32_register<V>)
518 auto const c = [&](
float value) { return ::wide::constant_like(x, value); };
519 auto const active = ::wide::mask_not(::wide::cmp_lt(x, c(Flush ? -87.33654022216796875f : -104.f)));
525 auto const overflow = ::wide::cmp_gt(x, c(88.72283172607421875f));
526 auto const in_range = ::wide::bit_and(active, ::wide::mask_not(overflow));
527 auto const replacement = ::wide::select(overflow,
528 c(std::bit_cast<float>(0x7f800000u)), c(0.f));
530 auto const n = ::wide::round_even(::wide::mul(r, c(1.4426950408889634f)));
531 r = ::wide::detail::madd(n, c(-0x1.62e400p-1f), r);
532 r = ::wide::detail::madd(n, c(-0x1.7f7d1cp-20f), r);
534 auto const y = exp_polynomial<Degree>(r);
535 return std::tuple{in_range, replacement, y, n};
543 template<
bool Flush = false,
unsigned Degree = 6, ::w
ide::promotable T>
544 requires (Degree >= 1 && Degree <= 7) && (::wide::detail::binary32_array<::wide::canonical_t<T>>)
547 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0) {
548 return std::remove_cvref_t<T>(input);
550 auto const x = ::wide::promote(input);
551 auto const [in_range, replacement, y, n] = detail::exp_reduced<Flush, Degree>(x);
552 return ::wide::demote<T>(::wide::detail::scale_exp(in_range, replacement, y, n));
559 template<
bool Flush = false, ::w
ide::promotable T>
560 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
562 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0) {
563 return std::remove_cvref_t<T>(input);
565 namespace w = ::wide;
566 auto const x = w::promote(input);
567 auto const c = [&](
float value) {
return w::constant_like(x, value); };
568 auto const active = w::mask_not(w::cmp_lt(x, c(Flush ? -126.f : -150.f)));
569 auto const overflow = w::cmp_ge(x, c(127.5f));
570 auto const in_range = w::bit_and(active, w::mask_not(overflow));
571 auto const replacement = w::select(overflow,
572 c(std::bit_cast<float>(0x7f800000u)), c(0.f));
573 auto const n = w::round_even(x);
574 auto const r = w::sub(x, n);
577 auto const y = ::math::horner(
578 c(0x1.3fa206p-13f), c(0x1.5f0b82p-10f), c(0x1.3b30ap-7f),
579 c(0x1.c6af76p-5f), c(0x1.ebfbd8p-3f), c(0x1.62e43p-1f), c(1.f))(r);
580 return w::demote<T>(w::detail::scale_exp(in_range, replacement, y, n));
587namespace math::detail {
588 template<
class V,std::
size_t N>
requires (::wide::detail::binary32_register<V>)
591 auto const word=w::bits(input);
592 auto const u=[&](std::uint32_t value) {
return w::constant_like(word,value);};
593 auto const magnitude=w::bit_and(word,u(0x7fffffffu));
596 auto const safe=w::select(w::cmp_lt(magnitude,u(0x41200000u)),magnitude,u(0x3f800000u));
597 auto const x=w::from_bits(safe);
598 auto const z=w::mul(x,x);
599 auto const interval=w::select(w::cmp_le(safe,u(0x40800000u)),
600 w::select(w::cmp_le(safe,u(0x40000000u)),
601 w::select(w::cmp_le(safe,u(0x3f800000u)),u(0),u(1)),
602 w::select(w::cmp_le(safe,u(0x40400000u)),u(2),u(3))),
603 w::select(w::cmp_le(safe,u(0x41000000u)),
604 w::select(w::cmp_le(safe,u(0x40c00000u)),u(4),u(5)),u(6)));
605 auto const coefficient=[&](std::uint32_t c0,std::uint32_t c1,std::uint32_t c2,
606 std::uint32_t c3,std::uint32_t c4,std::uint32_t c5,std::uint32_t c6) {
607 return w::detail::lift(w::detail::tanh_coefficient{{c0,c1,c2,c3,c4,c5,c6,c6}},interval);
609 auto const t=w::detail::madd(z,
610 coefficient(0x40000000u,0x3f000000u,0x3e800000u,0x3e800000u,0x3d800000u,0x3d800000u,0x3d000000u),
611 coefficient(0xbf800000u,0xbfa00000u,0xbfd00000u,0xc0480000u,0xbfd00000u,0xc0480000u,0xc0240000u));
612 auto h=coefficient(0x00000000u,0x00000000u,0x00000000u,0x00000000u,0xb9405fbfu,0x00000000u,0x00000000u);
613 h=w::detail::madd(h,t,coefficient(0x00000000u,0x38752140u,0x00000000u,0x00000000u,0x39ab4d5fu,0x00000000u,0x00000000u));
614 h=w::detail::madd(h,t,coefficient(0x00000000u,0xb9183513u,0xb947bd66u,0xb58f6d10u,0xb9c08feeu,0xb59171b1u,0x00000000u));
615 h=w::detail::madd(h,t,coefficient(0x34facb37u,0x398d9ee0u,0x39dfe5a4u,0x36863471u,0x3a2bf5fau,0x3671d749u,0x00000000u));
616 h=w::detail::madd(h,t,coefficient(0xb63a0d2du,0xba2fdf3au,0xba4a3864u,0xb758ec9du,0xbaa65b2au,0xb72757e1u,0xb811c415u));
617 h=w::detail::madd(h,t,coefficient(0x37813497u,0x3ae1130du,0x3ae2b84bu,0x384b3fb7u,0x3b1584c1u,0x380d4deeu,0x38c8e73cu));
618 h=w::detail::madd(h,t,coefficient(0xb8bfb3f8u,0xbb8bc302u,0xbb813c08u,0xb9404721u,0xbb86bc79u,0xb8f4646bu,0xb980a2b8u));
619 h=w::detail::madd(h,t,coefficient(0x3a0e6d24u,0x3c2d6773u,0x3c1129ceu,0x3a356f7bu,0x3bf71905u,0x39d46b54u,0x3a37296bu));
620 h=w::detail::madd(h,t,coefficient(0xbb535f6cu,0xbcd7a178u,0xbca3c0b8u,0xbb2d3d49u,0xbc67da1fu,0xbabdbc02u,0xbb06690eu));
621 h=w::detail::madd(h,t,coefficient(0x3c9d20e4u,0x3d86d45du,0x3d3bcdf4u,0x3c2a7e14u,0x3ce35ff2u,0x3bb1ed8au,0x3bcea86au));
622 h=w::detail::madd(h,t,coefficient(0xbded544du,0xbe2e2df9u,0xbde4f8bcu,0xbd36d397u,0xbd76f5e5u,0xbcb95c19u,0xbcb08499u));
623 h=w::detail::madd(h,t,coefficient(0x3f5c6e3eu,0x3f14c222u,0x3ec662fcu,0x3e9091d7u,0x3e48ced7u,0x3e10d0b5u,0x3de229ecu));
625 auto result=w::bits(w::mul(x,h));
626 auto const sign=w::bit_and(word,u(0x80000000u));
627 result=w::bit_or(w::min(result,u(0x3f800000u)),sign);
628 result=w::select(w::cmp_ge(magnitude,u(0x41200000u)),w::bit_or(sign,u(0x3f800000u)),result);
629 result=w::select(w::cmp_le(magnitude,u(0x39800000u)),word,result);
630 result=w::select(w::cmp_gt(magnitude,u(0x7f800000u)),u(0x7fc00000u),result);
631 return w::from_bits(result);
637 template<::w
ide::promotable T>
requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
639 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size==0)
return std::remove_cvref_t<T>(input);
640 else return ::wide::demote<T>(detail::tanh_kernel(::wide::promote(input)));
645namespace math::detail {
649 template<
class V, std::
size_t N>
650 requires (::wide::detail::binary32_register<V>)
652 std::array<V, N>
const & y, std::array<V, N>
const & x)
noexcept {
653 namespace w = ::wide;
654 auto const c = [&](
float value) {
return w::constant_like(y, value); };
655 auto const yw = w::bits(y), xw = w::bits(x);
656 auto const u = [&](std::uint32_t value) {
return w::constant_like(yw, value); };
657 auto const f = [&](std::uint32_t value) {
return c(std::bit_cast<float>(value)); };
658 auto ay = w::bit_and(yw, u(0x7fffffffu));
659 auto ax = w::bit_and(xw, u(0x7fffffffu));
660 ay = w::select(w::cmp_lt(ay, u(0x00800000u)), u(0), ay);
661 ax = w::select(w::cmp_lt(ax, u(0x00800000u)), u(0), ax);
662 auto const swap = w::cmp_gt(ay, ax);
663 auto const negative_x = w::cmp_ne(w::bit_and(xw, u(0x80000000u)), u(0));
664 auto const a = w::select(swap, ax, ay);
665 auto const b = w::select(swap, ay, ax);
666 auto const ratio = w::div(w::from_bits(a), w::from_bits(b));
667 auto const tiny = w::cmp_le(w::bits(ratio), u(0x39800000u));
668 auto const z = w::mul(ratio, ratio);
669 auto const h = ::math::horner(
670 f(0x3b390ccdu), f(0xbc82b80du), f(0x3d2e19b6u), f(0xbd995ffau),
671 f(0x3dd9ccf2u), f(0xbe116f9fu), f(0x3e4cb9a7u), f(0xbeaaaa5du))(z);
672 auto angle = w::detail::madd(w::mul(z, h), ratio, ratio);
673 angle = w::select(tiny, ratio, angle);
674 angle = w::select(swap, w::sub(f(0x3fc90fdbu), angle), angle);
675 angle = w::select(negative_x, w::sub(f(0x40490fdbu), angle), angle);
676 auto result = w::bits(angle);
677 auto const axis = w::select(negative_x, u(0x40490fdbu), u(0));
678 result = w::select(w::cmp_eq(ax, u(0)), u(0x3fc90fdbu), result);
679 result = w::select(w::cmp_eq(ax, u(0x7f800000u)), axis, result);
680 result = w::select(w::cmp_eq(ay, u(0x7f800000u)),
681 w::select(w::cmp_eq(ax, u(0x7f800000u)),
682 w::select(negative_x, u(0x4016cbe4u), u(0x3f490fdbu)), u(0x3fc90fdbu)), result);
683 result = w::select(w::cmp_eq(ay, u(0)), axis, result);
684 result = w::bit_or(result, w::bit_and(yw, u(0x80000000u)));
685 result = w::select(w::bit_or(w::cmp_gt(ay, u(0x7f800000u)),
686 w::cmp_gt(ax, u(0x7f800000u))), u(0x7fc00000u), result);
687 return w::from_bits(result);
693 template<::w
ide::promotable T>
694 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
696 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
697 return std::remove_cvref_t<T>(y);
698 else return ::wide::demote<T>(detail::atan2_kernel(::wide::promote(y), ::wide::promote(x)));
732 template<
bool Gain,
class V, std::
size_t N>
733 requires (::wide::detail::binary32_register<V>)
735 std::array<V, N>
const & input)
noexcept {
736 if constexpr (N == 0)
return input;
738 auto const c = [&](
float value) { return ::wide::constant_like(input, value); };
739 auto const w = [&](std::uint32_t bits) {
return c(std::bit_cast<float>(bits)); };
741 if constexpr (Gain) return ::wide::negate(input);
744 auto const n = ::wide::round_even(::wide::mul(x, w(0x3fb8aa3bu)));
745 auto const n_zero = ::wide::cmp_eq(n, c(0.f));
746 auto r = ::wide::detail::madd(n, w(0xbf317200u), x);
747 r = ::wide::detail::madd(n, w(0xb5bfbe8eu), r);
748 r = ::wide::select(n_zero, x, r);
749 auto const z = ::wide::mul(r, r);
750 auto h = ::wide::detail::madd(r, w(0x3493f27eu), w(0x3638ef1du));
751 h = ::wide::detail::madd(r, h, w(0x37d00d01u));
752 h = ::wide::detail::madd(r, h, w(0x39500d01u));
753 h = ::wide::detail::madd(r, h, w(0x3ab60b61u));
754 h = ::wide::detail::madd(r, h, w(0x3c088889u));
755 h = ::wide::detail::madd(r, h, w(0x3d2aaaabu));
756 h = ::wide::detail::madd(r, h, w(0x3e2aaaabu));
757 h = ::wide::detail::madd(r, h, w(0x3f000000u));
758 auto p = ::wide::detail::madd(z, h, r);
759 auto const bits = ::wide::bits(r);
760 auto const u = [&](std::uint32_t value) { return ::wide::constant_like(bits, value); };
761 auto const not_tiny = ::wide::cmp_gt(::wide::bit_and(bits, u(0x7fffffffu)), u(0x33000000u));
762 p = ::wide::select(not_tiny, p, r);
767 auto const factor_n = ::wide::max(n, c(-24.f));
768 auto const scale = ::wide::detail::exp_power(factor_n);
769 auto result = ::wide::detail::madd(scale, p, ::wide::sub(scale, c(1.f)));
770 result = ::wide::select(n_zero, p, result);
771 result = ::wide::select(::wide::cmp_eq(n, c(-25.f)),
772 ::wide::select(::wide::cmp_gt(p, c(0.f)), w(0xbf7fffffu), c(-1.f)), result);
773 result = ::wide::select(::wide::cmp_lt(n, c(-25.f)), c(-1.f), result);
775 result = ::wide::select(::wide::cmp_gt(x, c(88.37625885009765625f)),
776 w(0x7f800000u), result);
777 if constexpr (Gain) return ::wide::negate(result);
785 template<::w
ide::promotable T>
786 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
788 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
789 return std::remove_cvref_t<T>(input);
790 else return ::wide::demote<T>(detail::expm1_kernel<false>(::wide::promote(input)));
795 template<::w
ide::promotable T>
796 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
798 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
799 return std::remove_cvref_t<T>(input);
800 else return ::wide::demote<T>(detail::expm1_kernel<true>(::wide::promote(input)));
804namespace math::detail {
806 template<
class V, std::
size_t N>
807 requires (::wide::detail::binary32_register<V>)
809 namespace w = ::wide;
810 auto const c = [&](
float value) {
return w::constant_like(input, value); };
811 auto const word = w::bits(input);
812 auto const u = [&](std::uint32_t value) {
return w::constant_like(word, value); };
815 auto exponent = w::sub(w::right<23>(word), u(127));
816 auto mantissa = w::bit_or(w::bit_and(word, u(0x007fffffu)), u(0x3f800000u));
819 auto const upper = w::cmp_ge(mantissa, u(0x3fb504f3u));
820 mantissa = w::sub(mantissa, w::select(upper, u(0x00800000u), u(0)));
821 exponent = w::add(exponent, w::select(upper, u(1), u(0)));
822 auto const r = w::sub(w::from_bits(mantissa), c(1.f));
825 auto const h = ::math::horner(
826 c(-0x1.9c1d9ep-4f), c(0x1.729888p-3f), c(-0x1.8ac44cp-3f),
827 c(0x1.a55be8p-3f), c(-0x1.ea87d4p-3f), c(0x1.2767d4p-2f),
828 c(-0x1.715b1p-2f), c(0x1.ec713ap-2f), c(-0x1.71547p-1f),
829 c(0x1.715476p+0f))(r);
830 auto result = w::bits(w::detail::madd(r, h, w::detail::signed_float(exponent)));
831 auto const magnitude = w::bit_and(word, u(0x7fffffffu));
832 auto const sign = w::bit_and(word, u(0x80000000u));
833 result = w::select(w::cmp_eq(word, u(0x7f800000u)), word, result);
834 result = w::select(w::cmp_ne(sign, u(0)), u(0x7fc00000u), result);
835 result = w::select(w::cmp_lt(magnitude, u(0x00800000u)), u(0xff800000u), result);
836 result = w::select(w::cmp_gt(magnitude, u(0x7f800000u)), u(0x7fc00000u), result);
837 return w::from_bits(result);
845 template<
bool OnePlus,
class V, std::
size_t N>
846 requires (::wide::detail::binary32_register<V>)
848 namespace w = ::wide;
849 auto const c = [&](
float value) {
return w::constant_like(input, value); };
850 auto const word = w::bits(input);
851 auto const u = [&](std::uint32_t value) {
return w::constant_like(word, value); };
852 auto const magnitude = w::bit_and(word, u(0x7fffffffu));
853 auto const sign = w::bit_and(word, u(0x80000000u));
854 auto const valid = [&] {
855 if constexpr (OnePlus)
return w::bit_and(w::cmp_lt(magnitude, u(0x7f800000u)),
856 w::bit_or(w::cmp_eq(sign, u(0)), w::cmp_lt(magnitude, u(0x3f800000u))));
857 else return w::bit_and(w::cmp_ge(word, u(0x00800000u)), w::cmp_lt(word, u(0x7f800000u)));
859 auto const direct = [&] {
860 if constexpr (OnePlus)
return w::bit_and(valid, w::cmp_le(magnitude,
861 w::select(w::cmp_ne(sign, u(0)), u(0x3f000000u), u(0x3f800000u))));
864 auto const positive = [&] {
865 if constexpr (OnePlus)
return w::bits(w::add(c(1.f), w::from_bits(
866 w::select(w::bit_and(valid, w::mask_not(direct)), word, u(0)))));
867 else return w::select(valid, word, u(0x3f800000u));
869 auto exponent = w::sub(w::right<23>(positive), u(127));
870 auto mantissa = w::bit_or(w::bit_and(positive, u(0x007fffffu)), u(0x3f800000u));
871 auto const upper = w::cmp_ge(mantissa, u(0x3fc00000u));
872 mantissa = w::sub(mantissa, w::select(upper, u(0x00800000u), u(0)));
873 exponent = w::add(exponent, w::select(upper, u(1), u(0)));
874 auto const reduced = w::sub(w::from_bits(mantissa), c(1.f));
875 auto const argument = [&] {
876 if constexpr (OnePlus)
return w::select(direct, input, reduced);
879 auto const negative = w::cmp_ne(w::bit_and(w::bits(argument), u(0x80000000u)), u(0));
880 auto const coefficient = [&](std::uint32_t negative_word, std::uint32_t positive_word) {
881 return w::select(negative, c(std::bit_cast<float>(negative_word)), c(std::bit_cast<float>(positive_word)));
883 auto const square = w::mul(argument, argument);
884 auto const t = w::detail::madd(argument, coefficient(0x40800000u, 0x40000000u),
885 coefficient(0x3f800000u, 0xbf800000u));
886 auto h = coefficient(0x00000000u, 0xb29c7ee2u);
887 h = w::detail::madd(h, t, coefficient(0x00000000u, 0x3378ea39u));
888 h = w::detail::madd(h, t, coefficient(0xb44f5480u, 0xb3faaccbu));
889 h = w::detail::madd(h, t, coefficient(0x352754efu, 0x34c9e1cdu));
890 h = w::detail::madd(h, t, coefficient(0xb5bb75dbu, 0xb5b13b5eu));
891 h = w::detail::madd(h, t, coefficient(0x369a1c19u, 0x36902a0au));
892 h = w::detail::madd(h, t, coefficient(0xb7866f43u, 0xb76af011u));
893 h = w::detail::madd(h, t, coefficient(0x3861235au, 0x38423d8au));
894 h = w::detail::madd(h, t, coefficient(0xb93e98dfu, 0xb9225d51u));
895 h = w::detail::madd(h, t, coefficient(0x3a24a041u, 0x3a0988b0u));
896 h = w::detail::madd(h, t, coefficient(0xbb117f6au, 0xbaed1a41u));
897 h = w::detail::madd(h, t, coefficient(0x3c04b7c5u, 0x3bd13ce0u));
898 h = w::detail::madd(h, t, coefficient(0xbcfda364u, 0xbcbeef90u));
899 h = w::detail::madd(h, t, coefficient(0x3e029133u, 0x3db786beu));
900 h = w::detail::madd(h, t, coefficient(0xbf1a5884u, 0xbec19b82u));
901 auto const polynomial = w::detail::madd(square, h, argument);
902 auto const e = w::detail::signed_float(exponent);
903 auto const low = w::detail::madd(e, c(std::bit_cast<float>(0x35bfbe8eu)), polynomial);
904 auto result = w::bits(w::detail::madd(e, c(std::bit_cast<float>(0x3f317200u)), low));
905 if constexpr (OnePlus) {
906 result = w::select(direct, w::bits(polynomial), result);
907 result = w::select(w::cmp_le(magnitude, u(0x33000000u)), word, result);
908 result = w::select(w::cmp_eq(word, u(0xbf800000u)), u(0xff800000u), result);
909 result = w::select(w::cmp_eq(word, u(0x7f800000u)), word, result);
910 result = w::select(w::bit_or(w::cmp_gt(magnitude, u(0x7f800000u)),
911 w::bit_and(w::cmp_ne(sign, u(0)), w::cmp_gt(magnitude, u(0x3f800000u)))), u(0x7fc00000u), result);
913 result = w::select(w::cmp_eq(word, u(0x7f800000u)), word, result);
914 result = w::select(w::cmp_ne(sign, u(0)), u(0x7fc00000u), result);
915 result = w::select(w::cmp_lt(magnitude, u(0x00800000u)), u(0xff800000u), result);
916 result = w::select(w::cmp_gt(magnitude, u(0x7f800000u)), u(0x7fc00000u), result);
918 return w::from_bits(result);
923 template<::w
ide::promotable T>
requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
925 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
return std::remove_cvref_t<T>(input);
926 else return ::wide::demote<T>(detail::log_kernel<false>(::wide::promote(input)));
930 template<::w
ide::promotable T>
requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
932 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
return std::remove_cvref_t<T>(input);
933 else return ::wide::demote<T>(detail::log2_kernel(::wide::promote(input)));
936 template<::w
ide::promotable T>
requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
938 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
return std::remove_cvref_t<T>(input);
939 else return ::wide::demote<T>(detail::log_kernel<true>(::wide::promote(input)));
953 enum class trig_kind { sine, cosine, paired };
956 template<trig_kind K, ::w
ide::pack P>
958 auto const encoded = ::wide::bits(original);
959 auto const c = [&](
float value) { return ::wide::constant_like(original, value); };
960 auto const i = [&](std::uint32_t value) { return ::wide::constant_like(encoded, value); };
961 auto sign_sine = ::wide::bit_and(encoded, i(0x80000000u));
962 auto x = ::wide::from_bits(::wide::bit_and(encoded, i(0x7fffffffu)));
963 auto y = ::wide::mul(x, c(1.27323954473516f));
964 auto const j = ::wide::bit_and(::wide::add(::wide::detail::trig_integer(y), i(1)), i(0xfffffffeu));
965 y = ::wide::detail::signed_float(j);
966 sign_sine = ::wide::bit_xor(sign_sine, ::wide::left<29>(::wide::bit_and(j, i(4))));
967 auto const sign_cosine = ::wide::left<29>(::wide::bit_and(::wide::bit_xor(::wide::sub(j, i(2)), i(0xffffffffu)), i(4)));
969 if constexpr (K == trig_kind::cosine) quadrant = ::wide::sub(quadrant, i(2));
970 auto const mask = ::wide::mask_bits<std::uint32_t>(::wide::cmp_eq(::wide::bit_and(quadrant, i(2)), i(0)));
971 x = ::wide::detail::madd(y, c(-0.78515625f), x);
972 x = ::wide::detail::madd(y, c(-2.4187564849853515625e-4f), x);
973 x = ::wide::detail::madd(y, c(-3.77489497744594108e-8f), x);
974 auto const z = ::wide::mul(x, x);
975 auto cosine = ::wide::detail::madd(c(2.443315711809948e-5f), z, c(-1.388731625493765e-3f));
976 cosine = ::wide::detail::madd(cosine, z, c(4.166664568298827e-2f));
977 cosine = ::wide::mul(cosine, z);
978 cosine = ::wide::mul(cosine, z);
979 cosine = ::wide::sub(cosine, ::wide::mul(z, c(0.5f)));
980 cosine = ::wide::add(cosine, c(1.f));
981 auto sine = ::wide::detail::madd(c(-1.9515295891e-4f), z, c(8.3321608736e-3f));
982 sine = ::wide::detail::madd(sine, z, c(-1.6666654611e-1f));
983 sine = ::wide::mul(sine, z);
984 sine = ::wide::detail::madd(sine, x, x);
985 auto selected_sine = ::wide::from_bits(::wide::bit_and(mask, ::wide::bits(sine)));
986 auto selected_cosine = ::wide::from_bits(::wide::bit_and(::wide::bit_xor(mask, i(0xffffffffu)), ::wide::bits(cosine)));
987 if constexpr (K == trig_kind::paired) {
989 sine = ::wide::sub(sine, selected_sine);
990 cosine = ::wide::sub(cosine, selected_cosine);
991 selected_sine = ::wide::from_bits(::wide::bit_xor(::wide::bits(::wide::add(selected_cosine, selected_sine)), sign_sine));
992 selected_cosine = ::wide::from_bits(::wide::bit_xor(::wide::bits(::wide::add(cosine, sine)), sign_cosine));
993 return std::pair{selected_sine, selected_cosine};
994 }
else if constexpr (K == trig_kind::sine)
995 return ::wide::from_bits(::wide::bit_xor(::wide::bits(::wide::add(selected_cosine, selected_sine)), sign_sine));
996 else return ::wide::from_bits(::wide::bit_xor(::wide::bits(::wide::add(selected_cosine, selected_sine)), sign_cosine));
999 template<trig_kind K, ::w
ide::promotable T>
1000 requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1002 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0) {
1003 if constexpr (K == trig_kind::paired)
1004 return std::pair{std::remove_cvref_t<T>(input), std::remove_cvref_t<T>(input)};
1005 else return std::remove_cvref_t<T>(input);
1006 }
else if constexpr (K == trig_kind::paired) {
1007 auto const [sine, cosine] = trig<K>(::wide::promote(input));
1008 return std::pair{::wide::demote<T>(sine), ::wide::demote<T>(cosine)};
1009 }
else return ::wide::demote<T>(trig<K>(::wide::promote(input)));
1014 template<::w
ide::promotable T>
requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1016 return detail::trig_result<detail::trig_kind::sine>(input);
1019 template<::w
ide::promotable T>
requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1021 return detail::trig_result<detail::trig_kind::cosine>(input);
1024 template<::w
ide::promotable T>
requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1026 return detail::trig_result<detail::trig_kind::paired>(input);
1030 template<::w
ide::promotable T>
requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1032 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
1033 return std::remove_cvref_t<T>(input);
1035 auto bits = ::wide::bits(::wide::promote(input));
1036 auto const i = [&](std::uint32_t value) { return ::wide::constant_like(bits, value); };
1037 auto const exponent = ::wide::bit_and(bits, i(0x7f800000u));
1038 auto const zero = ::wide::mask_bits<std::uint32_t>(::wide::cmp_eq(exponent, i(0)));
1039 auto const clear = ::wide::bit_and(zero, i(0x007fffffu));
1040 bits = ::wide::bit_and(bits, ::wide::bit_xor(clear, i(0xffffffffu)));
1041 return ::wide::demote<T>(::wide::from_bits(bits));
1046#define NATIVE_PROMOTED_UNARY(name) \
1047 template<::wide::promotable T> requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>) \
1048 native_nodiscard native_inline constexpr auto name(T const & input) noexcept { \
1049 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0) \
1050 return std::remove_cvref_t<T>(input); \
1051 else return ::wide::demote<T>(::wide::name(::wide::promote(input))); \
1053 NATIVE_PROMOTED_UNARY(abs)
1054 NATIVE_PROMOTED_UNARY(sqrt)
1055 NATIVE_PROMOTED_UNARY(floor)
1056 NATIVE_PROMOTED_UNARY(ceil)
1057 NATIVE_PROMOTED_UNARY(trunc)
1058 NATIVE_PROMOTED_UNARY(round_even)
1059#undef NATIVE_PROMOTED_UNARY
1064 using ::math::horner;
1067 using ::math::expm1;
1068 using ::math::damping_gain;
1071 using ::math::log1p;
1073 using ::math::atan2;
1076 using ::math::sincos;
1077 using ::math::flush_to_zero;
#define native_inline
inline [[always_inline]]
#define native_nodiscard
C++17 [[nodiscard]].
constexpr auto mask_bits(simd< M, N, Arch > m) noexcept
typename mask_traits< std::remove_cvref_t< T > >::type mask
constexpr simd< float, N, Arch > masked_scaleb_zero(M mask, simd< float, N, Arch > value, simd< float, N, Arch > exponent) noexcept
constexpr simd< float, N, Arch > scaleb(simd< float, N, Arch > value, simd< float, N, Arch > exponent) noexcept
constexpr simd< float, N, Arch > masked_scaleb(M mask, simd< float, N, Arch > prior, simd< float, N, Arch > value, simd< float, N, Arch > exponent) noexcept
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
constexpr architecture arm
Select ARM feature metadata independently of the compilation target.
constexpr std::size_t sin_width
Suggested sine register count, matching the paired trigonometric kernel.
constexpr architecture x86
Select x86 feature metadata independently of the compilation target.
constexpr std::size_t log_width
Conservative independent register count for the logarithm kernel.
constexpr std::size_t cos_width
Suggested cosine register count, matching the paired trigonometric kernel.
constexpr architecture wasm
Select WebAssembly feature metadata independently of the compilation target.
constexpr simd< fp16, 32, Arch > fma(simd< fp16, 32, Arch > a, simd< fp16, 32, Arch > b, simd< fp16, 32, Arch > c) noexcept
constexpr std::size_t log1p_width
Conservative independent register count for cancellation-safe log1p.
constexpr std::size_t damping_gain_width
Suggested damping_gain register count, matching its expm1 graph.
constexpr std::size_t atan2_width
constexpr std::size_t log2_width
Suggested log2 register count, matching the logarithm recommendation.
constexpr std::size_t exp2_width
Recommended exp2 register count; its shorter reduction favors small batches.
constexpr std::size_t expm1_width
Conservative independent register count for cancellation-safe expm1.
constexpr std::size_t tanh_width
Conservative independent register count for tanh's table and polynomial.
constexpr std::size_t exp_width
constexpr std::size_t sincos_width
Recommended independent register count for paired sine and cosine.