native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
wide_math.h
1// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) AND BSL-1.0
3#pragma once
4#include "native/simd.h"
5#include "native/wide_pack.h"
6#include <tuple>
7
8namespace native {
9 namespace detail {
10 // Metadata only: recommendations can be queried for another ISA family
11 // without instantiating a foreign SIMD type or enabling its instructions.
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;
22 else return false;
23 }();
24
25 // A conservative starting point for kernels without a measured width sweep.
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;
29
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;
32 }
33
37 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
38 inline constexpr std::size_t exp_width = [] {
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};
43 }();
44
46 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
47 inline constexpr std::size_t exp2_width = detail::math_branching_width<T, K, A>;
48
50 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
51 inline constexpr std::size_t expm1_width = detail::math_polynomial_width<T, K, A>;
52
54 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
55 inline constexpr std::size_t damping_gain_width = expm1_width<T, K, A>;
56
58 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
59 inline constexpr std::size_t log_width = detail::math_polynomial_width<T, K, A>;
60
62 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
63 inline constexpr std::size_t log2_width = log_width<T, K, A>;
64
66 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
67 inline constexpr std::size_t log1p_width = detail::math_polynomial_width<T, K, A>;
68
70 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
71 inline constexpr std::size_t tanh_width = detail::math_branching_width<T, K, A>;
72
75 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
76 inline constexpr std::size_t atan2_width = detail::math_branching_width<T, K, A>;
77
79 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
80 inline constexpr std::size_t sincos_width =
81 detail::math_vector_shape<T, K, A> && A.family == arm && K == 4
82 ? 4 : detail::math_branching_width<T, K, A>;
83
85 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
86 inline constexpr std::size_t sin_width = sincos_width<T, K, A>;
87
89 template<class T, std::size_t K, isa A = NATIVE_BASELINE> requires (K > 0)
90 inline constexpr std::size_t cos_width = sincos_width<T, K, A>;
91}
92
93namespace math {
94 using ::native::exp_width;
95 using ::native::exp2_width;
96 using ::native::expm1_width;
98 using ::native::log_width;
99 using ::native::log2_width;
100 using ::native::log1p_width;
101 using ::native::tanh_width;
102 using ::native::atan2_width;
103 using ::native::sincos_width;
104 using ::native::sin_width;
105 using ::native::cos_width;
106}
107
108namespace wide::detail {
109 template<class V> struct native_ops;
110}
111#define NATIVE_BACKEND_BODY "native/wide_native_body.h"
112#include "native/simd/for_each_backend.h"
113#undef NATIVE_BACKEND_BODY
114
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>>); };
122
123 // Every batch is homogeneous. Select each array's element directly and
124 // broadcast native operands by reference, without tuple argument binding.
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...>();
131 }
132 template<std::size_t N,class A> consteval bool lift_size_matches() {
133 if constexpr (pack<A>) return shape_t<A>::size==N;
134 else return true;
135 }
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;
138 else {
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>;
142 }
143 }
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);
150 else return (value);
151 }
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)...);
155 }
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...)...}};
160 }
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...);
164 }
165
166#define NATIVE_WIDE_BINARY_OPERATION(name,bridge,op) \
167 struct name { \
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); } \
170 };
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) \
186 struct name { \
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); } \
189 };
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) \
195 struct name { \
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); } \
198 };
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
206 struct minimum {
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); }
209 };
210 struct maximum {
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); }
213 };
214 struct fused {
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); }
217 };
218 struct polynomial_madd {
219 template<class V>
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);
222 }
223 };
224 struct exp_power_operation {
225 template<class V>
226 native_inline constexpr auto operator()(V const & n) const { return native_ops<V>::exp_power(n); }
227 };
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); }
230 struct exp_scale {
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);
234 }
235 };
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);
239 }
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);
243 }
244 struct scale {
245 template<class M,class V> requires requires(M m,V a) { masked_scaleb_zero(m,a,a); }
246 native_inline constexpr auto operator()(M const & m,V const & a,V const & n) const { return native_ops<V>::scale(m,a,n); }
247 };
248 struct scale_all {
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); }
251 };
252 struct choose {
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); }
255 };
256 struct scale_merge {
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);
260 }
261 };
262 struct encode {
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); }
265 };
266 struct decode {
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); }
270 };
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); }
274 };
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); }
278 };
279 template<class T> struct mask_words {
280 template<class V> requires requires(V a) { ::native::mask_bits<T>(a); }
281 native_inline constexpr auto operator()(V const & a) const { return native_ops<V>::template mask_words<T>(a); }
282 };
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); }
286 };
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);
292 }
293 };
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); }
297 };
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); }
302
303}
304
305namespace wide {
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>
310 native_nodiscard native_inline constexpr V constant_like(std::array<V,N> const &, T value) noexcept {
311 return detail::native_ops<V>::constant(value);
312 }
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); \
317 }
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); \
339 }
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>
355 native_nodiscard native_inline constexpr auto fma(P const & a, Q const & b, R const & c) noexcept {
356 return detail::lift(detail::fused{},a,b,c);
357 }
360 template<class P,class Q,class R> requires detail::liftable<detail::scale,P,Q,R>
361 native_nodiscard native_inline constexpr auto masked_scaleb_zero(P const & m, Q const & a, R const & n) noexcept {
362 return detail::lift(detail::scale{},m,a,n);
363 }
366 template<class M,class P,class Q> requires detail::liftable<detail::choose,M,P,Q>
367 native_nodiscard native_inline constexpr auto select(M const & m,P const & a,Q const & b) noexcept {
368 return detail::lift(detail::choose{},m,a,b);
369 }
372 template<class M,class P,class Q,class R> requires detail::liftable<detail::scale_merge,M,P,Q,R>
373 native_nodiscard native_inline constexpr auto masked_scaleb(M const & m,P const & prior,Q const & a,R const & n) noexcept {
374 return detail::lift(detail::scale_merge{},m,prior,a,n);
375 }
378 template<unsigned Shift,class P> requires detail::liftable<detail::shift_left<Shift>,P>
379 native_nodiscard native_inline constexpr auto left(P const & a) noexcept {
380 return detail::lift(detail::shift_left<Shift>{},a);
381 }
383 template<unsigned Shift,class P> requires detail::liftable<detail::shift_right<Shift>,P>
384 native_nodiscard native_inline constexpr auto right(P const & a) noexcept {
385 return detail::lift(detail::shift_right<Shift>{},a);
386 }
389 template<class T,class P> requires detail::liftable<detail::mask_words<T>,P>
390 native_nodiscard native_inline constexpr auto mask_bits(P const & a) noexcept {
391 return detail::lift(detail::mask_words<T>{},a);
392 }
393
394 namespace detail {
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>;
403 }
404}
405
406namespace math {
407 namespace detail {
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>>));
415
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)), ...);
421 return h;
422 }
423
424 // Inline broadcasts with their consuming stages so scalar coefficient packs
425 // do not survive as temporary arrays in the generated code.
426 template<class P> struct horner_coefficient_conversion {
427 P const & input;
428
429 template<class C>
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);
438 else return value;
439 }
440 };
441
442 template<class T, class C0, class... C>
443 native_inline constexpr auto evaluate_horner(T const & z,
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));
451 } else {
452 return ::wide::demote<T>(horner_kernel(input,
453 coefficient(first), coefficient(rest)...));
454 }
455 }
456
457 template<class... C> struct horner_polynomial {
458 std::tuple<C...> coefficients;
459
460 template<::wide::promotable T>
461 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>) &&
462 (horner_coefficient<T, C> && ...)
463 native_nodiscard native_inline constexpr auto operator()(T const & z) const noexcept {
464 auto const & [...values] = coefficients;
465 return evaluate_horner(z, values...);
466 }
467 };
468 }
469
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>>) && ...)
481 native_nodiscard native_inline constexpr auto horner(C0 first, C... rest) noexcept {
482 return detail::horner_polynomial<C0, C...>{{first, rest...}};
483 }
484
485 namespace detail {
486 // Each degree shares the reduction and scaling graph. Sollya binary32 fits
487 // keep the constant exactly one; six is the default accuracy/cost choice.
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);
508 else
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);
512 }
513
514 // The single polynomial body, shared by generic and targeted entry points.
515 template<bool Flush, unsigned Degree = 6, class V, std::size_t N>
516 requires (Degree >= 1 && Degree <= 7) && (::wide::detail::binary32_register<V>)
517 native_nodiscard native_inline constexpr auto exp_reduced(std::array<V, N> const & x) noexcept {
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)));
520 // Classify independently; range flags are consumed only at the finish.
521 // Unordered comparisons leave NaNs on the arithmetic propagation path.
522 // The last binary32 input whose exact exponential rounds finite. n=128
523 // is valid: bounded reconstruction splits its scale into 2^127 and 2.
524 // Strict comparison keeps NaNs on the arithmetic propagation path.
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));
529 auto r = x;
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);
533
534 auto const y = exp_polynomial<Degree>(r);
535 return std::tuple{in_range, replacement, y, n};
536 }
537 }
538
543 template<bool Flush = false, unsigned Degree = 6, ::wide::promotable T>
544 requires (Degree >= 1 && Degree <= 7) && (::wide::detail::binary32_array<::wide::canonical_t<T>>)
545 native_nodiscard native_inline constexpr auto exp(T const & input) noexcept {
546 // MSVC's array<T,0> may construct a dummy T; an empty batch needs no work.
547 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0) {
548 return std::remove_cvref_t<T>(input);
549 } else {
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));
553 }
554 }
555
559 template<bool Flush = false, ::wide::promotable T>
560 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
561 native_nodiscard native_inline constexpr auto exp2(T const & input) noexcept {
562 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0) {
563 return std::remove_cvref_t<T>(input);
564 } else {
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);
575 // Sollya fpminimax(2^x,[|1,...,6|],[|single...|],[-1/2;1/2],relative,1).
576 // All coefficients are binary32; the fixed constant preserves exp2(0).
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));
581 }
582 }
583}
584// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
585// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) AND BSL-1.0
586// Adapted from FTZ's seven-interval tanh polynomial, with native arithmetic.
587namespace math::detail {
588 template<class V,std::size_t N> requires (::wide::detail::binary32_register<V>)
589 native_nodiscard native_inline constexpr auto tanh_kernel(std::array<V,N> const & input) noexcept {
590 namespace w=::wide;
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));
594 // Bound large inputs before squaring; the tiny-region result below keeps
595 // original words, including subnormals under the caller's FP environment.
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);
608 };
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));
624
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);
632 }
633}
634namespace math {
637 template<::wide::promotable T> requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
638 native_nodiscard native_inline constexpr auto tanh(T const & input) noexcept {
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)));
641 }
642}
643
644// Adapted coefficient use from SLEEF 3.9.0 atan2kf; Boost 1.0 notice below.
645namespace math::detail {
646 // Raw subnormal inputs are signed zero. Normal operands use one packed
647 // min/max ratio, with no software flushing or changes to FP controls.
648 // A tiny finite result may underflow according to the caller's FP mode.
649 template<class V, std::size_t N>
650 requires (::wide::detail::binary32_register<V>)
651 native_nodiscard native_inline constexpr auto atan2_kernel(
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);
688 }
689}
690namespace math {
693 template<::wide::promotable T>
694 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
695 native_nodiscard native_inline constexpr auto atan2(T const & y, T const & x) noexcept {
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)));
699 }
700}
701
702/*
703Copyright Naoki Shibata and contributors 2010 - 2025.
704Boost Software License - Version 1.0 - August 17th, 2003
705
706Permission is hereby granted, free of charge, to any person or organization
707obtaining a copy of the software and accompanying documentation covered by
708this license (the "Software") to use, reproduce, display, distribute,
709execute, and transmit the Software, and to prepare derivative works of the
710Software, and to permit third-parties to whom the Software is furnished to
711do so, all subject to the following:
712
713The copyright notices in the Software and this entire statement, including
714the above license grant, this restriction and the following disclaimer,
715must be included in all copies of the Software, in whole or in part, and
716all derivative works of the Software, unless such copies or derivative
717works are solely in the form of machine-executable object code generated by
718a source language processor.
719
720THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
721IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
722FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
723SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
724FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
725ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
726DEALINGS IN THE SOFTWARE.
727*/
728
729// Adapted from FTZ's cancellation-safe expm1 and reduced log/log1p graphs.
730namespace math {
731 namespace detail {
732 template<bool Gain, class V, std::size_t N>
733 requires (::wide::detail::binary32_register<V>)
734 native_nodiscard native_inline constexpr auto expm1_kernel(
735 std::array<V, N> const & input) noexcept {
736 if constexpr (N == 0) return input;
737 else {
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)); };
740 auto const x = [&] {
741 if constexpr (Gain) return ::wide::negate(input);
742 else return input;
743 }();
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);
763
764 // Only the reconstruction factor is bounded below; the input and
765 // polynomial are untouched. max's unordered case supplies -24 so a NaN
766 // polynomial propagates while the factor remains well-defined.
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);
774 // Retain general exp's accepted early positive overflow threshold.
775 result = ::wide::select(::wide::cmp_gt(x, c(88.37625885009765625f)),
776 w(0x7f800000u), result);
777 if constexpr (Gain) return ::wide::negate(result);
778 else return result;
779 }
780 }
781 }
782
785 template<::wide::promotable T>
786 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
787 native_nodiscard native_inline constexpr auto expm1(T const & input) noexcept {
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)));
791 }
792
795 template<::wide::promotable T>
796 requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
797 native_nodiscard native_inline constexpr auto damping_gain(T const & input) noexcept {
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)));
801 }
802}
803
804namespace math::detail {
805 // Direct base-two reduction and a newly generated Sollya polynomial.
806 template<class V, std::size_t N>
807 requires (::wide::detail::binary32_register<V>)
808 native_nodiscard native_inline constexpr auto log2_kernel(std::array<V, N> const & input) noexcept {
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); };
813 // The sign bit adds a harmless 256 for negative inputs, replaced below.
814 // Delay the magnitude mask so it need not stay live through the polynomial.
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));
817 // Fold at binary32 sqrt(2), keeping log2(m) close to [-0.5, 0.5].
818 // The bit reduction is finite even for inputs classified below.
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));
823 // Sollya 8.0 fpminimax of log2(1+r)/r, degree 9, binary32
824 // coefficients, absolute error on [-0x1.2bec34p-2, 0x1.a82790p-2].
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);
838 }
839
840 // Normal inputs retain the FTZ hardware polynomial and reduction graph.
841 // There is no software flushing between arithmetic operations and no FP
842 // control change. log treats subnormal inputs as signed zero; log1p returns
843 // the original bits for |x|<=2^-25, including signed zero and subnormals.
844 // NaNs and domain errors return the FTZ graph's canonical quiet NaN.
845 template<bool OnePlus, class V, std::size_t N>
846 requires (::wide::detail::binary32_register<V>)
847 native_nodiscard native_inline constexpr auto log_kernel(std::array<V, N> const & input) noexcept {
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)));
858 }();
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))));
862 else return valid;
863 }();
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));
868 }();
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);
877 else return reduced;
878 }();
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)));
882 };
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);
912 } else {
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);
917 }
918 return w::from_bits(result);
919 }
920}
921namespace math {
923 template<::wide::promotable T> requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
924 native_nodiscard native_inline constexpr auto log(T const & input) noexcept {
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)));
927 }
930 template<::wide::promotable T> requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
931 native_nodiscard native_inline constexpr auto log2(T const & input) noexcept {
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)));
934 }
936 template<::wide::promotable T> requires (::wide::detail::binary32_array<::wide::canonical_t<T>>)
937 native_nodiscard native_inline constexpr auto log1p(T const & input) noexcept {
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)));
940 }
941}
942
943// SPDX-FileCopyrightText: 2012 Giovanni Garberoglio
944// SPDX-FileCopyrightText: 2017 Edward Kmett
945// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
946// SPDX-License-Identifier: Zlib AND BSD-2-Clause
947// Included by wide_math.h after the lifted native operations.
948// Altered source: the original trig reducer and polynomials now operate on
949// standard arrays, preserving each binary32 dependency stage.
950
951namespace math {
952 namespace detail {
953 enum class trig_kind { sine, cosine, paired };
954
955 // Precondition: every lane is finite and |x| < 8192.
956 template<trig_kind K, ::wide::pack P>
957 native_nodiscard native_inline constexpr auto trig(P const & original) noexcept {
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)));
968 auto quadrant = j;
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) {
988 // Preserve subtraction selection, including its signed-zero effects.
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));
997 }
998
999 template<trig_kind K, ::wide::promotable T>
1000 requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1001 native_nodiscard native_inline constexpr auto trig_result(T const & input) noexcept {
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)));
1010 }
1011 }
1012
1014 template<::wide::promotable T> requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1015 native_nodiscard native_inline constexpr auto sin(T const & input) noexcept {
1016 return detail::trig_result<detail::trig_kind::sine>(input);
1017 }
1019 template<::wide::promotable T> requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1020 native_nodiscard native_inline constexpr auto cos(T const & input) noexcept {
1021 return detail::trig_result<detail::trig_kind::cosine>(input);
1022 }
1024 template<::wide::promotable T> requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1025 native_nodiscard native_inline constexpr auto sincos(T const & input) noexcept {
1026 return detail::trig_result<detail::trig_kind::paired>(input);
1027 }
1028
1030 template<::wide::promotable T> requires (::wide::detail::binary32_pack<::wide::canonical_t<T>>)
1031 native_nodiscard native_inline constexpr auto flush_to_zero(T const & input) noexcept {
1032 if constexpr (::wide::detail::shape_t<::wide::canonical_t<T>>::size == 0)
1033 return std::remove_cvref_t<T>(input);
1034 else {
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));
1042 }
1043 }
1044
1045 // These primitives retain their native leaf semantics inside one wide stage.
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))); \
1052 }
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
1060}
1061
1062namespace wide {
1063 // Qualified convenience aliases; standard arrays keep their ordinary ADL.
1064 using ::math::horner;
1065 using ::math::exp;
1066 using ::math::exp2;
1067 using ::math::expm1;
1068 using ::math::damping_gain;
1069 using ::math::log;
1070 using ::math::log2;
1071 using ::math::log1p;
1072 using ::math::tanh;
1073 using ::math::atan2;
1074 using ::math::sin;
1075 using ::math::cos;
1076 using ::math::sincos;
1077 using ::math::flush_to_zero;
1078}
1079
1080/*
1081 AVX implementation of sin, cos, sincos, exp and log
1082
1083 Based on "sse_mathfun.h", by Julien Pommier
1084 http://gruntthepeon.free.fr/ssemath/
1085
1086 Copyright (C) 2012 Giovanni Garberoglio
1087 Interdisciplinary Laboratory for Computational Science (LISC)
1088 Fondazione Bruno Kessler and University of Trento
1089 via Sommarive, 18
1090 I-38123 Trento (Italy)
1091
1092 This software is provided 'as-is', without any express or implied
1093 warranty. In no event will the authors be held liable for any damages
1094 arising from the use of this software.
1095
1096 Permission is granted to anyone to use this software for any purpose,
1097 including commercial applications, and to alter it and redistribute it
1098 freely, subject to the following restrictions:
1099
1100 1. The origin of this software must not be misrepresented; you must not
1101 claim that you wrote the original software. If you use this software
1102 in a product, an acknowledgment in the product documentation would be
1103 appreciated but is not required.
1104 2. Altered source versions must be plainly marked as such, and must not be
1105 misrepresented as being the original software.
1106 3. This notice may not be removed or altered from any source distribution.
1107*/
1108
1109/* RTS repository license (retained verbatim):
1110Software License Agreement (BSD 2-Clause License)
1111========================================
1112
1113Copyright 2017 Edward Kmett
1114
1115Redistribution and use in source and binary forms, with or without
1116modification, are permitted provided that the following conditions are met:
1117
1118 * Redistributions of source code must retain the above copyright
1119 notice, this list of conditions and the following disclaimer.
1120
1121 * Redistributions in binary form must reproduce the above copyright
1122 notice, this list of conditions and the following disclaimer in the
1123 documentation and/or other materials provided with the distribution.
1124
1125THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1126ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1127WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1128DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY
1129DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1130(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1131LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1132ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1133(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1134SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1135*/
#define native_inline
inline [[always_inline]]
Definition attributes.h:212
#define native_nodiscard
C++17 [[nodiscard]].
Definition attributes.h:189
constexpr auto mask_bits(simd< M, N, Arch > m) noexcept
typename mask_traits< std::remove_cvref_t< T > >::type mask
Definition mask_traits.h:22
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.
Definition config.h:28
constexpr std::size_t sin_width
Suggested sine register count, matching the paired trigonometric kernel.
Definition wide_math.h:86
constexpr architecture x86
Select x86 feature metadata independently of the compilation target.
Definition config.h:26
constexpr std::size_t log_width
Conservative independent register count for the logarithm kernel.
Definition wide_math.h:59
constexpr std::size_t cos_width
Suggested cosine register count, matching the paired trigonometric kernel.
Definition wide_math.h:90
constexpr architecture wasm
Select WebAssembly feature metadata independently of the compilation target.
Definition config.h:30
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.
Definition wide_math.h:67
constexpr std::size_t damping_gain_width
Suggested damping_gain register count, matching its expm1 graph.
Definition wide_math.h:55
constexpr std::size_t atan2_width
Definition wide_math.h:76
constexpr std::size_t log2_width
Suggested log2 register count, matching the logarithm recommendation.
Definition wide_math.h:63
constexpr std::size_t exp2_width
Recommended exp2 register count; its shorter reduction favors small batches.
Definition wide_math.h:47
constexpr std::size_t expm1_width
Conservative independent register count for cancellation-safe expm1.
Definition wide_math.h:51
constexpr std::size_t tanh_width
Conservative independent register count for tanh's table and polynomial.
Definition wide_math.h:71
constexpr std::size_t exp_width
Definition wide_math.h:38
constexpr std::size_t sincos_width
Recommended independent register count for paired sine and cosine.
Definition wide_math.h:80