File indexing completed on 2026-11-07 10:36:59
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #ifndef XSIMD_COMMON_MATH_HPP
0013 #define XSIMD_COMMON_MATH_HPP
0014
0015 #include "../xsimd_scalar.hpp"
0016 #include "./xsimd_common_details.hpp"
0017 #include "./xsimd_common_trigo.hpp"
0018
0019 #include <type_traits>
0020
0021 namespace xsimd
0022 {
0023
0024 namespace kernel
0025 {
0026
0027 using namespace types;
0028
0029 template <class A, class T, class>
0030 XSIMD_INLINE batch<T, A> abs(batch<T, A> const& self, requires_arch<common>) noexcept
0031 {
0032 if (std::is_unsigned<T>::value)
0033 return self;
0034 else
0035 {
0036 auto sign = bitofsign(self);
0037 auto inv = self ^ sign;
0038 return inv - sign;
0039 }
0040 }
0041
0042 template <class A, class T>
0043 XSIMD_INLINE batch<T, A> abs(batch<std::complex<T>, A> const& z, requires_arch<common>) noexcept
0044 {
0045 return hypot(z.real(), z.imag());
0046 }
0047
0048
0049 namespace detail
0050 {
0051 template <class A, class T>
0052 XSIMD_INLINE batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, std::true_type, std::false_type) noexcept
0053 {
0054 return (x & y) + ((x ^ y) >> 1);
0055 }
0056
0057 template <class A, class T>
0058 XSIMD_INLINE batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, std::true_type, std::true_type) noexcept
0059 {
0060
0061
0062 auto t = (x & y) + ((x ^ y) >> 1);
0063 auto t_u = bitwise_cast<std::make_unsigned_t<T>>(t);
0064 auto avg = t + (bitwise_cast<T>(t_u >> (8 * sizeof(T) - 1)) & (x ^ y));
0065 return avg;
0066 }
0067
0068 template <class A, class T>
0069 XSIMD_INLINE batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, std::false_type, std::true_type) noexcept
0070 {
0071 return (x + y) / 2;
0072 }
0073 }
0074
0075 template <class A, class T>
0076 XSIMD_INLINE batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, requires_arch<common>) noexcept
0077 {
0078 return detail::avg(x, y, typename std::is_integral<T>::type {}, typename std::is_signed<T>::type {});
0079 }
0080
0081
0082 namespace detail
0083 {
0084 template <class A, class T>
0085 XSIMD_INLINE batch<T, A> avgr(batch<T, A> const& x, batch<T, A> const& y, std::true_type) noexcept
0086 {
0087 constexpr unsigned shift = 8 * sizeof(T) - 1;
0088 auto adj = std::is_signed<T>::value ? ((x ^ y) & 0x1) : (((x ^ y) << shift) >> shift);
0089 return ::xsimd::kernel::avg(x, y, A {}) + adj;
0090 }
0091
0092 template <class A, class T>
0093 XSIMD_INLINE batch<T, A> avgr(batch<T, A> const& x, batch<T, A> const& y, std::false_type) noexcept
0094 {
0095 return ::xsimd::kernel::avg(x, y, A {});
0096 }
0097 }
0098
0099 template <class A, class T>
0100 XSIMD_INLINE batch<T, A> avgr(batch<T, A> const& x, batch<T, A> const& y, requires_arch<common>) noexcept
0101 {
0102 return detail::avgr(x, y, typename std::is_integral<T>::type {});
0103 }
0104
0105
0106 template <class A, class T>
0107 XSIMD_INLINE batch<T, A> batch_cast(batch<T, A> const& self, batch<T, A> const&, requires_arch<common>) noexcept
0108 {
0109 return self;
0110 }
0111
0112 namespace detail
0113 {
0114 template <class A, class T_out, class T_in>
0115 XSIMD_INLINE batch<T_out, A> batch_cast(batch<T_in, A> const& self, batch<T_out, A> const& out, requires_arch<common>, with_fast_conversion) noexcept
0116 {
0117 return fast_cast(self, out, A {});
0118 }
0119 #if defined(__clang__) || __GNUC__
0120 template <class A, class T_out, class T_in>
0121 XSIMD_INLINE batch<T_out, A> batch_cast(batch<T_in, A> const& self, batch<T_out, A> const&, requires_arch<common>, with_slow_conversion) noexcept
0122 __attribute__((no_sanitize("undefined")));
0123 #endif
0124 template <class A, class T_out, class T_in>
0125 XSIMD_INLINE batch<T_out, A> batch_cast(batch<T_in, A> const& self, batch<T_out, A> const&, requires_arch<common>, with_slow_conversion) noexcept
0126 {
0127 static_assert(!std::is_same<T_in, T_out>::value, "there should be no conversion for this type combination");
0128 using batch_type_in = batch<T_in, A>;
0129 using batch_type_out = batch<T_out, A>;
0130 static_assert(batch_type_in::size == batch_type_out::size, "compatible sizes");
0131 alignas(A::alignment()) T_in buffer_in[batch_type_in::size];
0132 alignas(A::alignment()) T_out buffer_out[batch_type_out::size];
0133 self.store_aligned(&buffer_in[0]);
0134 for (size_t i = 0; i < batch_type_in::size; ++i)
0135 buffer_out[i] = static_cast<T_out>(buffer_in[i]);
0136 return batch_type_out::load_aligned(buffer_out);
0137 }
0138
0139 }
0140
0141 template <class A, class T_out, class T_in>
0142 XSIMD_INLINE batch<T_out, A> batch_cast(batch<T_in, A> const& self, batch<T_out, A> const& out, requires_arch<common>) noexcept
0143 {
0144 return detail::batch_cast(self, out, A {}, detail::conversion_type<A, T_in, T_out> {});
0145 }
0146
0147
0148 template <class A, class T>
0149 XSIMD_INLINE batch<T, A> bitofsign(batch<T, A> const& self, requires_arch<common>) noexcept
0150 {
0151 static_assert(std::is_integral<T>::value, "int type implementation");
0152 if (std::is_unsigned<T>::value)
0153 return batch<T, A>(0);
0154 else
0155 return self >> (T)(8 * sizeof(T) - 1);
0156 }
0157
0158 template <class A>
0159 XSIMD_INLINE batch<float, A> bitofsign(batch<float, A> const& self, requires_arch<common>) noexcept
0160 {
0161 return self & constants::signmask<batch<float, A>>();
0162 }
0163 template <class A>
0164 XSIMD_INLINE batch<double, A> bitofsign(batch<double, A> const& self, requires_arch<common>) noexcept
0165 {
0166 return self & constants::signmask<batch<double, A>>();
0167 }
0168
0169
0170 template <class A, class T>
0171 XSIMD_INLINE batch<T, A> bitwise_cast(batch<T, A> const& self, batch<T, A> const&, requires_arch<common>) noexcept
0172 {
0173 return self;
0174 }
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186 template <class A>
0187 XSIMD_INLINE batch<float, A> cbrt(batch<float, A> const& self, requires_arch<common>) noexcept
0188 {
0189 using batch_type = batch<float, A>;
0190 batch_type z = abs(self);
0191 #ifndef XSIMD_NO_DENORMALS
0192 auto denormal = z < constants::smallestposval<batch_type>();
0193 z = select(denormal, z * constants::twotonmb<batch_type>(), z);
0194 batch_type f = select(denormal, constants::twotonmbo3<batch_type>(), batch_type(1.));
0195 #endif
0196 const batch_type CBRT2(bit_cast<float>(0x3fa14518));
0197 const batch_type CBRT4(bit_cast<float>(0x3fcb2ff5));
0198 const batch_type CBRT2I(bit_cast<float>(0x3f4b2ff5));
0199 const batch_type CBRT4I(bit_cast<float>(0x3f214518));
0200 using i_type = as_integer_t<batch_type>;
0201 i_type e;
0202 batch_type x = frexp(z, e);
0203 x = detail::horner<batch_type,
0204 0x3ece0609,
0205 0x3f91eb77,
0206 0xbf745265,
0207 0x3f0bf0fe,
0208 0xbe09e49a>(x);
0209 auto flag = e >= i_type(0);
0210 i_type e1 = abs(e);
0211 i_type rem = e1;
0212 e1 /= i_type(3);
0213 rem -= e1 * i_type(3);
0214 e = e1 * sign(e);
0215 const batch_type cbrt2 = select(batch_bool_cast<float>(flag), CBRT2, CBRT2I);
0216 const batch_type cbrt4 = select(batch_bool_cast<float>(flag), CBRT4, CBRT4I);
0217 batch_type fact = select(batch_bool_cast<float>(rem == i_type(1)), cbrt2, batch_type(1.));
0218 fact = select(batch_bool_cast<float>(rem == i_type(2)), cbrt4, fact);
0219 x = ldexp(x * fact, e);
0220 x -= (x - z / (x * x)) * batch_type(1.f / 3.f);
0221 #ifndef XSIMD_NO_DENORMALS
0222 x = (x | bitofsign(self)) * f;
0223 #else
0224 x = x | bitofsign(self);
0225 #endif
0226 #ifndef XSIMD_NO_INFINITIES
0227 return select(self == batch_type(0.) || isinf(self), self, x);
0228 #else
0229 return select(self == batch_type(0.), self, x);
0230 #endif
0231 }
0232
0233 template <class A>
0234 XSIMD_INLINE batch<double, A> cbrt(batch<double, A> const& self, requires_arch<common>) noexcept
0235 {
0236 using batch_type = batch<double, A>;
0237 batch_type z = abs(self);
0238 #ifndef XSIMD_NO_DENORMALS
0239 auto denormal = z < constants::smallestposval<batch_type>();
0240 z = select(denormal, z * constants::twotonmb<batch_type>(), z);
0241 batch_type f = select(denormal, constants::twotonmbo3<batch_type>(), batch_type(1.));
0242 #endif
0243 const batch_type CBRT2(bit_cast<double>(int64_t(0x3ff428a2f98d728b)));
0244 const batch_type CBRT4(bit_cast<double>(int64_t(0x3ff965fea53d6e3d)));
0245 const batch_type CBRT2I(bit_cast<double>(int64_t(0x3fe965fea53d6e3d)));
0246 const batch_type CBRT4I(bit_cast<double>(int64_t(0x3fe428a2f98d728b)));
0247 using i_type = as_integer_t<batch_type>;
0248 i_type e;
0249 batch_type x = frexp(z, e);
0250 x = detail::horner<batch_type,
0251 0x3fd9c0c12122a4feull,
0252 0x3ff23d6ee505873aull,
0253 0xbfee8a4ca3ba37b8ull,
0254 0x3fe17e1fc7e59d58ull,
0255 0xbfc13c93386fdff6ull>(x);
0256 auto flag = e >= typename i_type::value_type(0);
0257 i_type e1 = abs(e);
0258 i_type rem = e1;
0259 e1 /= i_type(3);
0260 rem -= e1 * i_type(3);
0261 e = e1 * sign(e);
0262 const batch_type cbrt2 = select(batch_bool_cast<double>(flag), CBRT2, CBRT2I);
0263 const batch_type cbrt4 = select(batch_bool_cast<double>(flag), CBRT4, CBRT4I);
0264 batch_type fact = select(batch_bool_cast<double>(rem == i_type(1)), cbrt2, batch_type(1.));
0265 fact = select(batch_bool_cast<double>(rem == i_type(2)), cbrt4, fact);
0266 x = ldexp(x * fact, e);
0267 x -= (x - z / (x * x)) * batch_type(1. / 3.);
0268 x -= (x - z / (x * x)) * batch_type(1. / 3.);
0269 #ifndef XSIMD_NO_DENORMALS
0270 x = (x | bitofsign(self)) * f;
0271 #else
0272 x = x | bitofsign(self);
0273 #endif
0274 #ifndef XSIMD_NO_INFINITIES
0275 return select(self == batch_type(0.) || isinf(self), self, x);
0276 #else
0277 return select(self == batch_type(0.), self, x);
0278 #endif
0279 }
0280
0281
0282 template <class A, class T>
0283 XSIMD_INLINE batch<T, A> clip(batch<T, A> const& self, batch<T, A> const& lo, batch<T, A> const& hi, requires_arch<common>) noexcept
0284 {
0285 return min(hi, max(self, lo));
0286 }
0287
0288
0289 template <class A, class T, class = std::enable_if_t<std::is_floating_point<T>::value>>
0290 XSIMD_INLINE batch<T, A> copysign(batch<T, A> const& self, batch<T, A> const& other, requires_arch<common>) noexcept
0291 {
0292 return abs(self) | bitofsign(other);
0293 }
0294
0295
0296
0297 namespace detail
0298 {
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308 template <class B>
0309 struct erf_kernel;
0310
0311 template <class A>
0312 struct erf_kernel<batch<float, A>>
0313 {
0314 using batch_type = batch<float, A>;
0315
0316
0317 static XSIMD_INLINE batch_type erf1(const batch_type& x) noexcept
0318 {
0319 return detail::horner<batch_type,
0320 0x3f906eba,
0321 0xbec0937e,
0322 0x3de70f22,
0323 0xbcdb61f4,
0324 0x3ba4468d,
0325 0xba1fc83b
0326 >(x);
0327 }
0328
0329
0330
0331 static XSIMD_INLINE batch_type erfc2(const batch_type& x) noexcept
0332 {
0333 return detail::horner<batch_type,
0334 0x3f0a0e8b,
0335 0xbf918a62,
0336 0x3e243828,
0337 0x3ec4ca6e,
0338 0x3e1175c7,
0339 0x3e2006f0,
0340 0xbfaea865,
0341 0x4050b063,
0342 0xc0cd1a85,
0343 0x40d67e3b,
0344 0xc0283611
0345 >(x);
0346 }
0347
0348 static XSIMD_INLINE batch_type erfc3(const batch_type& x) noexcept
0349 {
0350 return (batch_type(1.) - x) * detail::horner<batch_type,
0351 0x3f7ffffe,
0352 0xbe036d7e,
0353 0xbfa11698,
0354 0xbffc9284,
0355 0xc016c985,
0356 0x3f2cff3b,
0357 0xc010d956,
0358 0x401b5680,
0359 0x41aa8e55
0360 >(x);
0361 }
0362 };
0363
0364 template <class A>
0365 struct erf_kernel<batch<double, A>>
0366 {
0367 using batch_type = batch<double, A>;
0368
0369
0370 static XSIMD_INLINE batch_type erf1(const batch_type& x) noexcept
0371 {
0372 return detail::horner<batch_type,
0373 0x3ff20dd750429b61ull,
0374 0x3fc16500f106c0a5ull,
0375 0x3fa4a59a4f02579cull,
0376 0x3f53b7664358865aull,
0377 0x3f110512d5b20332ull
0378 >(x)
0379 / detail::horner<batch_type,
0380 0x3ff0000000000000ull,
0381 0x3fdd0a84eb1ca867ull,
0382 0x3fb64536ca92ea2full,
0383 0x3f8166f75999dbd1ull,
0384 0x3f37ea4332348252ull
0385 >(x);
0386 }
0387
0388
0389
0390 static XSIMD_INLINE batch_type erfc2(const batch_type& x) noexcept
0391 {
0392 return detail::horner<batch_type,
0393 0x3feffffffbbb552bull,
0394 0x3ff54dfe9b258a60ull,
0395 0x3fec1986509e687bull,
0396 0x3fd53dd7a67c7e9full,
0397 0x3fb2488a6b5cb5e5ull,
0398 0x3f7cf4cfe0aacbb4ull,
0399 0x0ull
0400 >(x)
0401 / detail::horner<batch_type,
0402 0x3ff0000000000000ull,
0403 0x4003adeae79b9708ull,
0404 0x40053b1052dca8bdull,
0405 0x3ff9e677c2777c3cull,
0406 0x3fe307622fcff772ull,
0407 0x3fc033c113a7deeeull,
0408 0x3f89a996639b0d00ull
0409 >(x);
0410 }
0411
0412
0413
0414 static XSIMD_INLINE batch_type erfc3(const batch_type& x) noexcept
0415 {
0416 return detail::horner<batch_type,
0417 0x3fefff5a9e697ae2ull,
0418 0x3ff9fa202deb88e5ull,
0419 0x3ff44744306832aeull,
0420 0x3fe29be1cff90d94ull,
0421 0x3fc42210f88b9d43ull,
0422 0x3f971d0907ea7a92ull,
0423 0x0ll
0424 >(x)
0425 / detail::horner<batch_type,
0426 0x3ff0000000000000ull,
0427 0x400602f24bf3fdb6ull,
0428 0x400afd487397568full,
0429 0x400315ffdfd5ce91ull,
0430 0x3ff0cfd4cb6cde9full,
0431 0x3fd1d7ab774bb837ull,
0432 0x3fa47bd61bbb3843ull
0433 >(x);
0434 }
0435
0436
0437
0438 static XSIMD_INLINE batch_type erfc4(const batch_type& x) noexcept
0439 {
0440 return detail::horner<batch_type,
0441 0xbc7e4ad1ec7d0000ll,
0442 0x3fe20dd750429a16ll,
0443 0x3db60000e984b501ll,
0444 0xbfd20dd753ae5dfdll,
0445 0x3e907e71e046a820ll,
0446 0x3fdb1494cac06d39ll,
0447 0x3f34a451701654f1ll,
0448 0xbff105e6b8ef1a63ll,
0449 0x3fb505a857e9ccc8ll,
0450 0x40074fbabc514212ll,
0451 0x4015ac7631f7ac4fll,
0452 0xc0457e03041e9d8bll,
0453 0x4055803d26c4ec4fll,
0454 0xc0505fce04ec4ec5ll
0455 >(x);
0456 }
0457 };
0458 }
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469 template <class A>
0470 XSIMD_INLINE batch<float, A> erf(batch<float, A> const& self, requires_arch<common>) noexcept
0471 {
0472 using batch_type = batch<float, A>;
0473 batch_type x = abs(self);
0474 batch_type r1(0.);
0475 auto test1 = x < batch_type(2.f / 3.f);
0476 if (any(test1))
0477 {
0478 r1 = self * detail::erf_kernel<batch_type>::erf1(x * x);
0479 if (all(test1))
0480 return r1;
0481 }
0482 batch_type z = x / (batch_type(1.) + x);
0483 z -= batch_type(0.4f);
0484 batch_type r2 = batch_type(1.) - exp(-x * x) * detail::erf_kernel<batch_type>::erfc2(z);
0485 r2 = select(self < batch_type(0.), -r2, r2);
0486 r1 = select(test1, r1, r2);
0487 #ifndef XSIMD_NO_INFINITIES
0488 r1 = select(xsimd::isinf(self), sign(self), r1);
0489 #endif
0490 return r1;
0491 }
0492
0493 template <class A>
0494 XSIMD_INLINE batch<double, A> erf(batch<double, A> const& self, requires_arch<common>) noexcept
0495 {
0496 using batch_type = batch<double, A>;
0497 batch_type x = abs(self);
0498 batch_type xx = x * x;
0499 batch_type lim1(0.65);
0500 batch_type lim2(2.2);
0501 auto test1 = x < lim1;
0502 batch_type r1(0.);
0503 if (any(test1))
0504 {
0505 r1 = self * detail::erf_kernel<batch_type>::erf1(xx);
0506 if (all(test1))
0507 return r1;
0508 }
0509 auto test2 = x < lim2;
0510 auto test3 = test2 && !test1;
0511 batch_type ex = exp(-xx);
0512 if (any(test3))
0513 {
0514 batch_type z = batch_type(1.) - ex * detail::erf_kernel<batch_type>::erfc2(x);
0515 batch_type r2 = select(self < batch_type(0.), -z, z);
0516 r1 = select(test1, r1, r2);
0517 if (all(test1 || test3))
0518 return r1;
0519 }
0520 batch_type z = batch_type(1.) - ex * detail::erf_kernel<batch_type>::erfc3(x);
0521 z = select(self < batch_type(0.), -z, z);
0522 #ifndef XSIMD_NO_INFINITIES
0523 z = select(xsimd::isinf(self), sign(self), z);
0524 #endif
0525 return select(test2, r1, z);
0526 }
0527
0528
0529 template <class A>
0530 XSIMD_INLINE batch<float, A> erfc(batch<float, A> const& self, requires_arch<common>) noexcept
0531 {
0532 using batch_type = batch<float, A>;
0533 batch_type x = abs(self);
0534 auto test0 = self < batch_type(0.);
0535 batch_type r1(0.);
0536 auto test1 = 3.f * x < 2.f;
0537 batch_type z = x / (batch_type(1.) + x);
0538 if (any(test1))
0539 {
0540 r1 = detail::erf_kernel<batch_type>::erfc3(z);
0541 if (all(test1))
0542 return select(test0, batch_type(2.) - r1, r1);
0543 }
0544
0545 z -= batch_type(0.4f);
0546 batch_type r2 = exp(-x * x) * detail::erf_kernel<batch_type>::erfc2(z);
0547 r1 = select(test1, r1, r2);
0548 #ifndef XSIMD_NO_INFINITIES
0549 r1 = select(x == constants::infinity<batch_type>(), batch_type(0.), r1);
0550 #endif
0551 return select(test0, batch_type(2.) - r1, r1);
0552 }
0553
0554 template <class A>
0555 XSIMD_INLINE batch<double, A> erfc(batch<double, A> const& self, requires_arch<common>) noexcept
0556 {
0557 using batch_type = batch<double, A>;
0558 batch_type x = abs(self);
0559 batch_type xx = x * x;
0560 batch_type lim1(0.65);
0561 batch_type lim2(2.2);
0562 auto test0 = self < batch_type(0.);
0563 auto test1 = x < lim1;
0564 batch_type r1(0.);
0565 if (any(test1))
0566 {
0567 r1 = batch_type(1.) - x * detail::erf_kernel<batch_type>::erf1(xx);
0568 if (all(test1))
0569 return select(test0, batch_type(2.) - r1, r1);
0570 }
0571 auto test2 = x < lim2;
0572 auto test3 = test2 && !test1;
0573 batch_type ex = exp(-xx);
0574 if (any(test3))
0575 {
0576 batch_type z = ex * detail::erf_kernel<batch_type>::erfc2(x);
0577 r1 = select(test1, r1, z);
0578 if (all(test1 || test3))
0579 return select(test0, batch_type(2.) - r1, r1);
0580 }
0581 batch_type z = ex * detail::erf_kernel<batch_type>::erfc3(x);
0582 r1 = select(test2, r1, z);
0583 #ifndef XSIMD_NO_INFINITIES
0584 r1 = select(x == constants::infinity<batch_type>(), batch_type(0.), r1);
0585 #endif
0586 return select(test0, batch_type(2.) - r1, r1);
0587 }
0588
0589
0590 namespace detail
0591 {
0592
0593 template <class B>
0594 struct estrin
0595 {
0596 B x;
0597
0598 template <typename... Ts>
0599 XSIMD_INLINE B operator()(const Ts&... coefs) noexcept
0600 {
0601 return eval(coefs...);
0602 }
0603
0604 private:
0605 XSIMD_INLINE B eval(const B& c0) noexcept
0606 {
0607 return c0;
0608 }
0609
0610 XSIMD_INLINE B eval(const B& c0, const B& c1) noexcept
0611 {
0612 return fma(x, c1, c0);
0613 }
0614
0615 template <size_t... Is, class Tuple>
0616 XSIMD_INLINE B eval(std::index_sequence<Is...>, const Tuple& tuple)
0617 {
0618 return estrin { x * x }(std::get<Is>(tuple)...);
0619 }
0620
0621 template <class... Args>
0622 XSIMD_INLINE B eval(const std::tuple<Args...>& tuple) noexcept
0623 {
0624 return eval(std::make_index_sequence<sizeof...(Args)>(), tuple);
0625 }
0626
0627 template <class... Args>
0628 XSIMD_INLINE B eval(const std::tuple<Args...>& tuple, const B& c0) noexcept
0629 {
0630 return eval(std::tuple_cat(tuple, std::make_tuple(eval(c0))));
0631 }
0632
0633 template <class... Args>
0634 XSIMD_INLINE B eval(const std::tuple<Args...>& tuple, const B& c0, const B& c1) noexcept
0635 {
0636 return eval(std::tuple_cat(tuple, std::make_tuple(eval(c0, c1))));
0637 }
0638
0639 template <class... Args, class... Ts>
0640 XSIMD_INLINE B eval(const std::tuple<Args...>& tuple, const B& c0, const B& c1, const Ts&... coefs) noexcept
0641 {
0642 return eval(std::tuple_cat(tuple, std::make_tuple(eval(c0, c1))), coefs...);
0643 }
0644
0645 template <class... Ts>
0646 XSIMD_INLINE B eval(const B& c0, const B& c1, const Ts&... coefs) noexcept
0647 {
0648 return eval(std::make_tuple(eval(c0, c1)), coefs...);
0649 }
0650 };
0651 }
0652
0653 template <class T, class A, uint64_t... Coefs>
0654 XSIMD_INLINE batch<T, A> estrin(const batch<T, A>& self) noexcept
0655 {
0656 using batch_type = batch<T, A>;
0657 return detail::estrin<batch_type> { self }(detail::coef<batch_type, Coefs>()...);
0658 }
0659
0660
0661
0662
0663
0664
0665
0666
0667
0668
0669
0670 namespace detail
0671 {
0672 enum exp_reduction_tag
0673 {
0674 exp_tag,
0675 exp2_tag,
0676 exp10_tag
0677 };
0678
0679 template <class B, exp_reduction_tag Tag>
0680 struct exp_reduction_base;
0681
0682 template <class B>
0683 struct exp_reduction_base<B, exp_tag>
0684 {
0685 static constexpr B maxlog() noexcept
0686 {
0687 return constants::maxlog<B>();
0688 }
0689
0690 static constexpr B minlog() noexcept
0691 {
0692 return constants::minlog<B>();
0693 }
0694 };
0695
0696 template <class B>
0697 struct exp_reduction_base<B, exp10_tag>
0698 {
0699 static constexpr B maxlog() noexcept
0700 {
0701 return constants::maxlog10<B>();
0702 }
0703
0704 static constexpr B minlog() noexcept
0705 {
0706 return constants::minlog10<B>();
0707 }
0708 };
0709
0710 template <class B>
0711 struct exp_reduction_base<B, exp2_tag>
0712 {
0713 static constexpr B maxlog() noexcept
0714 {
0715 return constants::maxlog2<B>();
0716 }
0717
0718 static constexpr B minlog() noexcept
0719 {
0720 return constants::minlog2<B>();
0721 }
0722 };
0723
0724 template <class T, class A, exp_reduction_tag Tag>
0725 struct exp_reduction;
0726
0727 template <class A>
0728 struct exp_reduction<float, A, exp_tag> : exp_reduction_base<batch<float, A>, exp_tag>
0729 {
0730 using batch_type = batch<float, A>;
0731 static XSIMD_INLINE batch_type approx(const batch_type& x) noexcept
0732 {
0733 batch_type y = detail::horner<batch_type,
0734 0x3f000000,
0735 0x3e2aa9a5,
0736 0x3d2aa957,
0737 0x3c098d8b,
0738 0x3ab778cf
0739 >(x);
0740 return ++fma(y, x * x, x);
0741 }
0742
0743 static XSIMD_INLINE batch_type reduce(const batch_type& a, batch_type& x) noexcept
0744 {
0745 batch_type k = nearbyint(constants::invlog_2<batch_type>() * a);
0746 detail::reassociation_barrier(k, "compensated exp range reduction");
0747 x = fnma(k, constants::log_2hi<batch_type>(), a);
0748 detail::reassociation_barrier(x, "compensated exp range reduction");
0749 x = fnma(k, constants::log_2lo<batch_type>(), x);
0750 return k;
0751 }
0752 };
0753
0754 template <class A>
0755 struct exp_reduction<float, A, exp10_tag> : exp_reduction_base<batch<float, A>, exp10_tag>
0756 {
0757 using batch_type = batch<float, A>;
0758 static XSIMD_INLINE batch_type approx(const batch_type& x) noexcept
0759 {
0760 return ++(detail::horner<batch_type,
0761 0x40135d8e,
0762 0x4029a926,
0763 0x400237da,
0764 0x3f95eb4c,
0765 0x3f0aacef,
0766 0x3e54dff1
0767 >(x)
0768 * x);
0769 }
0770
0771 static XSIMD_INLINE batch_type reduce(const batch_type& a, batch_type& x) noexcept
0772 {
0773 batch_type k = nearbyint(constants::invlog10_2<batch_type>() * a);
0774 detail::reassociation_barrier(k, "compensated exp10 range reduction");
0775 x = fnma(k, constants::log10_2hi<batch_type>(), a);
0776 detail::reassociation_barrier(x, "compensated exp10 range reduction");
0777 x -= k * constants::log10_2lo<batch_type>();
0778 return k;
0779 }
0780 };
0781
0782 template <class A>
0783 struct exp_reduction<float, A, exp2_tag> : exp_reduction_base<batch<float, A>, exp2_tag>
0784 {
0785 using batch_type = batch<float, A>;
0786 static XSIMD_INLINE batch_type approx(const batch_type& x) noexcept
0787 {
0788 batch_type y = detail::horner<batch_type,
0789 0x3e75fdf1,
0790 0x3d6356eb,
0791 0x3c1d9422,
0792 0x3ab01218,
0793 0x3922c8c4
0794 >(x);
0795 return ++fma(y, x * x, x * constants::log_2<batch_type>());
0796 }
0797
0798 static XSIMD_INLINE batch_type reduce(const batch_type& a, batch_type& x) noexcept
0799 {
0800 batch_type k = nearbyint(a);
0801 detail::reassociation_barrier(k, "compensated exp2 range reduction");
0802 x = (a - k);
0803 return k;
0804 }
0805 };
0806
0807 template <class A>
0808 struct exp_reduction<double, A, exp_tag> : exp_reduction_base<batch<double, A>, exp_tag>
0809 {
0810 using batch_type = batch<double, A>;
0811 static XSIMD_INLINE batch_type approx(const batch_type& x) noexcept
0812 {
0813 batch_type t = x * x;
0814 return fnma(t,
0815 detail::horner<batch_type,
0816 0x3fc555555555553eull,
0817 0xbf66c16c16bebd93ull,
0818 0x3f11566aaf25de2cull,
0819 0xbebbbd41c5d26bf1ull,
0820 0x3e66376972bea4d0ull>(t),
0821 x);
0822 }
0823
0824 static XSIMD_INLINE batch_type reduce(const batch_type& a, batch_type& hi, batch_type& lo, batch_type& x) noexcept
0825 {
0826 batch_type k = nearbyint(constants::invlog_2<batch_type>() * a);
0827 detail::reassociation_barrier(k, "compensated exp range reduction");
0828 hi = fnma(k, constants::log_2hi<batch_type>(), a);
0829 detail::reassociation_barrier(hi, "compensated exp range reduction");
0830 lo = k * constants::log_2lo<batch_type>();
0831 x = hi - lo;
0832 return k;
0833 }
0834
0835 static XSIMD_INLINE batch_type finalize(const batch_type& x, const batch_type& c, const batch_type& hi, const batch_type& lo) noexcept
0836 {
0837 return batch_type(1.) - (((lo - (x * c) / (batch_type(2.) - c)) - hi));
0838 }
0839 };
0840
0841 template <class A>
0842 struct exp_reduction<double, A, exp10_tag> : exp_reduction_base<batch<double, A>, exp10_tag>
0843 {
0844 using batch_type = batch<double, A>;
0845 static XSIMD_INLINE batch_type approx(const batch_type& x) noexcept
0846 {
0847 batch_type xx = x * x;
0848 batch_type px = x * detail::horner<batch_type, 0x40a2b4798e134a01ull, 0x40796b7a050349e4ull, 0x40277d9474c55934ull, 0x3fa4fd75f3062dd4ull>(xx);
0849 batch_type x2 = px / (detail::horner1<batch_type, 0x40a03f37650df6e2ull, 0x4093e05eefd67782ull, 0x405545fdce51ca08ull>(xx) - px);
0850 return ++(x2 + x2);
0851 }
0852
0853 static XSIMD_INLINE batch_type reduce(const batch_type& a, batch_type&, batch_type&, batch_type& x) noexcept
0854 {
0855 batch_type k = nearbyint(constants::invlog10_2<batch_type>() * a);
0856 detail::reassociation_barrier(k, "compensated exp10 range reduction");
0857 x = fnma(k, constants::log10_2hi<batch_type>(), a);
0858 detail::reassociation_barrier(x, "compensated exp10 range reduction");
0859 x = fnma(k, constants::log10_2lo<batch_type>(), x);
0860 return k;
0861 }
0862
0863 static XSIMD_INLINE batch_type finalize(const batch_type&, const batch_type& c, const batch_type&, const batch_type&) noexcept
0864 {
0865 return c;
0866 }
0867 };
0868
0869 template <class A>
0870 struct exp_reduction<double, A, exp2_tag> : exp_reduction_base<batch<double, A>, exp2_tag>
0871 {
0872 using batch_type = batch<double, A>;
0873 static XSIMD_INLINE batch_type approx(const batch_type& x) noexcept
0874 {
0875 batch_type t = x * x;
0876 return fnma(t,
0877 detail::horner<batch_type,
0878 0x3fc555555555553eull,
0879 0xbf66c16c16bebd93ull,
0880 0x3f11566aaf25de2cull,
0881 0xbebbbd41c5d26bf1ull,
0882 0x3e66376972bea4d0ull>(t),
0883 x);
0884 }
0885
0886 static XSIMD_INLINE batch_type reduce(const batch_type& a, batch_type&, batch_type&, batch_type& x) noexcept
0887 {
0888 batch_type k = nearbyint(a);
0889 x = (a - k) * constants::log_2<batch_type>();
0890 detail::reassociation_barrier(x, "keep reduced exponent ordered before finalize");
0891 return k;
0892 }
0893
0894 static XSIMD_INLINE batch_type finalize(const batch_type& x, const batch_type& c, const batch_type&, const batch_type&) noexcept
0895 {
0896 return batch_type(1.) + x + x * c / (batch_type(2.) - c);
0897 }
0898 };
0899
0900 template <exp_reduction_tag Tag, class A>
0901 XSIMD_INLINE batch<float, A> exp(batch<float, A> const& self) noexcept
0902 {
0903 using batch_type = batch<float, A>;
0904 using reducer_t = exp_reduction<float, A, Tag>;
0905 batch_type x;
0906 batch_type k = reducer_t::reduce(self, x);
0907 x = reducer_t::approx(x);
0908 x = select(self <= reducer_t::minlog(), batch_type(0.), ldexp(x, to_int(k)));
0909 #ifndef __FAST_MATH__
0910 x = select(self >= reducer_t::maxlog(), constants::infinity<batch_type>(), x);
0911 #endif
0912 return x;
0913 }
0914
0915 template <exp_reduction_tag Tag, class A>
0916 XSIMD_INLINE batch<double, A> exp(batch<double, A> const& self) noexcept
0917 {
0918 using batch_type = batch<double, A>;
0919 using reducer_t = exp_reduction<double, A, Tag>;
0920 batch_type hi, lo, x;
0921 batch_type k = reducer_t::reduce(self, hi, lo, x);
0922 batch_type c = reducer_t::approx(x);
0923 c = reducer_t::finalize(x, c, hi, lo);
0924 c = select(self <= reducer_t::minlog(), batch_type(0.), ldexp(c, to_int(k)));
0925 #ifndef __FAST_MATH__
0926 c = select(self >= reducer_t::maxlog(), constants::infinity<batch_type>(), c);
0927 #endif
0928 return c;
0929 }
0930 }
0931
0932 template <class A, class T>
0933 XSIMD_INLINE batch<T, A> exp(batch<T, A> const& self, requires_arch<common>) noexcept
0934 {
0935 return detail::exp<detail::exp_tag>(self);
0936 }
0937
0938 template <class A, class T>
0939 XSIMD_INLINE batch<std::complex<T>, A> exp(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
0940 {
0941 using batch_type = batch<std::complex<T>, A>;
0942 auto isincos = sincos(self.imag());
0943 return exp(self.real()) * batch_type(std::get<1>(isincos), std::get<0>(isincos));
0944 }
0945
0946
0947 template <class A, class T>
0948 XSIMD_INLINE batch<T, A> exp10(batch<T, A> const& self, requires_arch<common>) noexcept
0949 {
0950 using batch_type = batch<T, A>;
0951 batch_type out = detail::exp<detail::exp10_tag>(self);
0952 detail::reassociation_barrier(out, "prevent folding exp10 for literal inputs");
0953 return out;
0954 }
0955
0956
0957 template <class A, class T>
0958 XSIMD_INLINE batch<T, A> exp2(batch<T, A> const& self, requires_arch<common>) noexcept
0959 {
0960 return detail::exp<detail::exp2_tag>(self);
0961 }
0962
0963
0964 namespace detail
0965 {
0966
0967
0968
0969
0970
0971
0972
0973
0974
0975 template <class A>
0976 static XSIMD_INLINE batch<float, A> expm1(const batch<float, A>& a) noexcept
0977 {
0978 using batch_type = batch<float, A>;
0979 batch_type k = nearbyint(constants::invlog_2<batch_type>() * a);
0980 batch_type x = fnma(k, constants::log_2hi<batch_type>(), a);
0981 x = fnma(k, constants::log_2lo<batch_type>(), x);
0982 batch_type hx = x * batch_type(0.5);
0983 batch_type hxs = x * hx;
0984 batch_type r = detail::horner<batch_type,
0985 0X3F800000UL,
0986 0XBD08887FUL,
0987 0X3ACF6DB4UL
0988 >(hxs);
0989 batch_type t = fnma(r, hx, batch_type(3.));
0990 batch_type e = hxs * ((r - t) / (batch_type(6.) - x * t));
0991 e = fms(x, e, hxs);
0992 using i_type = as_integer_t<batch_type>;
0993 i_type ik = to_int(k);
0994 batch_type two2mk = ::xsimd::bitwise_cast<float>((constants::maxexponent<batch_type>() - ik) << constants::nmb<batch_type>());
0995 batch_type y = batch_type(1.) - two2mk - (e - x);
0996 return ldexp(y, ik);
0997 }
0998
0999 template <class A>
1000 static XSIMD_INLINE batch<double, A> expm1(const batch<double, A>& a) noexcept
1001 {
1002 using batch_type = batch<double, A>;
1003 batch_type k = nearbyint(constants::invlog_2<batch_type>() * a);
1004 batch_type hi = fnma(k, constants::log_2hi<batch_type>(), a);
1005 batch_type lo = k * constants::log_2lo<batch_type>();
1006 batch_type x = hi - lo;
1007 batch_type hxs = x * x * batch_type(0.5);
1008 batch_type r = detail::horner<batch_type,
1009 0X3FF0000000000000ULL,
1010 0XBFA11111111110F4ULL,
1011 0X3F5A01A019FE5585ULL,
1012 0XBF14CE199EAADBB7ULL,
1013 0X3ED0CFCA86E65239ULL,
1014 0XBE8AFDB76E09C32DULL>(hxs);
1015 batch_type t = batch_type(3.) - r * batch_type(0.5) * x;
1016 batch_type e = hxs * ((r - t) / (batch_type(6) - x * t));
1017 batch_type c = (hi - x) - lo;
1018 e = (x * (e - c) - c) - hxs;
1019 using i_type = as_integer_t<batch_type>;
1020 i_type ik = to_int(k);
1021 batch_type two2mk = ::xsimd::bitwise_cast<double>((constants::maxexponent<batch_type>() - ik) << constants::nmb<batch_type>());
1022 batch_type ct1 = batch_type(1.) - two2mk - (e - x);
1023 batch_type ct2 = ++(x - (e + two2mk));
1024 batch_type y = select(k < batch_type(20.), ct1, ct2);
1025 return ldexp(y, ik);
1026 }
1027
1028 }
1029
1030 template <class A, class T>
1031 XSIMD_INLINE batch<T, A> expm1(batch<T, A> const& self, requires_arch<common>) noexcept
1032 {
1033 using batch_type = batch<T, A>;
1034 auto x = detail::expm1(self);
1035 #ifndef __FAST_MATH__
1036 x = select(self > constants::maxlog<batch_type>(), constants::infinity<batch_type>(), x);
1037 #endif
1038 return select(self < constants::logeps<batch_type>(), batch_type(-1.), x);
1039 }
1040
1041 template <class A, class T>
1042 XSIMD_INLINE batch<std::complex<T>, A> expm1(const batch<std::complex<T>, A>& z, requires_arch<common>) noexcept
1043 {
1044 using batch_type = batch<std::complex<T>, A>;
1045 using real_batch = typename batch_type::real_batch;
1046 real_batch isin = sin(z.imag());
1047 real_batch rem1 = expm1(z.real());
1048 real_batch re = rem1 + 1.;
1049 real_batch si = sin(z.imag() * 0.5);
1050 return { rem1 - 2. * re * si * si, re * isin };
1051 }
1052
1053
1054 template <class A, class T>
1055 XSIMD_INLINE batch<std::complex<T>, A> polar(const batch<T, A>& r, const batch<T, A>& theta, requires_arch<common>) noexcept
1056 {
1057 auto sincosTheta = sincos(theta);
1058 return { r * sincosTheta.second, r * sincosTheta.first };
1059 }
1060
1061
1062 template <class A, class T>
1063 XSIMD_INLINE batch<T, A> fdim(batch<T, A> const& self, batch<T, A> const& other, requires_arch<common>) noexcept
1064 {
1065 return fmax(batch<T, A>(0), self - other);
1066 }
1067
1068
1069 template <class A, class T>
1070 XSIMD_INLINE batch<T, A> fmod(batch<T, A> const& self, batch<T, A> const& other, requires_arch<common>) noexcept
1071 {
1072 return fnma(trunc(self / other), other, self);
1073 }
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085 template <class A, class T>
1086 XSIMD_INLINE batch<T, A> frexp(const batch<T, A>& self, batch<as_integer_t<T>, A>& exp, requires_arch<common>) noexcept
1087 {
1088 using batch_type = batch<T, A>;
1089 using int_type = as_integer_t<T>;
1090 using i_type = batch<int_type, A>;
1091 i_type m1f = constants::mask1frexp<batch_type>();
1092 i_type r1 = m1f & ::xsimd::bitwise_cast<int_type>(self);
1093 batch_type x = self & ::xsimd::bitwise_cast<T>(~m1f);
1094 exp = (r1 >> constants::nmb<batch_type>()) - constants::maxexponentm1<batch_type>();
1095 exp = select(batch_bool_cast<typename i_type::value_type>(self != batch_type(0.)), exp, i_type(typename i_type::value_type(0)));
1096 return select((self != batch_type(0.)), x | ::xsimd::bitwise_cast<T>(constants::mask2frexp<batch_type>()), batch_type(0.));
1097 }
1098
1099
1100 template <class A, class T>
1101 XSIMD_INLINE batch<T, A> from_bool(batch_bool<T, A> const& self, requires_arch<common>) noexcept
1102 {
1103 return batch<T, A>((typename batch<T, A>::register_type)self.data) & batch<T, A>(1);
1104 }
1105
1106
1107 template <class T, class A, uint64_t... Coefs>
1108 XSIMD_INLINE batch<T, A> horner(const batch<T, A>& self) noexcept
1109 {
1110 return detail::horner<batch<T, A>, Coefs...>(self);
1111 }
1112
1113
1114 template <class A, class T>
1115 XSIMD_INLINE batch<T, A> hypot(batch<T, A> const& self, batch<T, A> const& other, requires_arch<common>) noexcept
1116 {
1117 return sqrt(fma(self, self, other * other));
1118 }
1119
1120
1121 template <class A, class T, class ITy>
1122 XSIMD_INLINE batch<T, A> ipow(batch<T, A> const& self, ITy other, requires_arch<common>) noexcept
1123 {
1124 return ::xsimd::detail::ipow(self, other);
1125 }
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137 template <class A, class T>
1138 XSIMD_INLINE batch<T, A> ldexp(const batch<T, A>& self, const batch<as_integer_t<T>, A>& other, requires_arch<common>) noexcept
1139 {
1140 using batch_type = batch<T, A>;
1141 using itype = as_integer_t<batch_type>;
1142 itype ik = other + constants::maxexponent<T>();
1143 ik = ik << constants::nmb<T>();
1144 return self * ::xsimd::bitwise_cast<T>(ik);
1145 }
1146
1147
1148 template <class A, class T>
1149 XSIMD_INLINE batch<T, A> lgamma(batch<T, A> const& self, requires_arch<common>) noexcept;
1150
1151 namespace detail
1152 {
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162 template <class A>
1163 static XSIMD_INLINE batch<float, A> gammalnB(const batch<float, A>& x) noexcept
1164 {
1165 return horner<batch<float, A>,
1166 0x3ed87730,
1167 0x3ea51a64,
1168 0xbd89f07e,
1169 0x3ca89ed8,
1170 0xbbf164fd,
1171 0x3b3ba883,
1172 0xbaabeab1,
1173 0x3a1ebb94
1174 >(x);
1175 }
1176
1177 template <class A>
1178 static XSIMD_INLINE batch<float, A> gammalnC(const batch<float, A>& x) noexcept
1179 {
1180 return horner<batch<float, A>,
1181 0xbf13c468,
1182 0x3f528d34,
1183 0xbecd27a8,
1184 0x3e8a898b,
1185 0xbe53c04f,
1186 0x3e2d4dab,
1187 0xbe22d329,
1188 0x3e0c3c4f
1189 >(x);
1190 }
1191
1192 template <class A>
1193 static XSIMD_INLINE batch<float, A> gammaln2(const batch<float, A>& x) noexcept
1194 {
1195 return horner<batch<float, A>,
1196 0x3daaaa94,
1197 0xbb358701,
1198 0x3a31fd69
1199 >(x);
1200 }
1201
1202 template <class A>
1203 static XSIMD_INLINE batch<double, A> gammaln1(const batch<double, A>& x) noexcept
1204 {
1205 return horner<batch<double, A>,
1206 0xc12a0c675418055eull,
1207 0xc13a45890219f20bull,
1208 0xc131bc82f994db51ull,
1209 0xc1143d73f89089e5ull,
1210 0xc0e2f234355bb93eull,
1211 0xc09589018ff36761ull
1212 >(x)
1213 / horner<batch<double, A>,
1214 0xc13ece4b6a11e14aull,
1215 0xc1435255892ff34cull,
1216 0xc131628671950043ull,
1217 0xc10aeb84b9744c9bull,
1218 0xc0d0aa0d7b89d757ull,
1219 0xc075fd0d1cf312b2ull,
1220 0x3ff0000000000000ull
1221 >(x);
1222 }
1223
1224 template <class A>
1225 static XSIMD_INLINE batch<double, A> gammalnA(const batch<double, A>& x) noexcept
1226 {
1227 return horner<batch<double, A>,
1228 0x3fb555555555554bull,
1229 0xbf66c16c16b0a5a1ull,
1230 0x3f4a019f20dc5ebbull,
1231 0xbf437fbdb580e943ull,
1232 0x3f4a985027336661ull
1233 >(x);
1234 }
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245 template <class B>
1246 struct lgamma_impl;
1247
1248 template <class A>
1249 struct lgamma_impl<batch<float, A>>
1250 {
1251 using batch_type = batch<float, A>;
1252 static XSIMD_INLINE batch_type compute(const batch_type& a) noexcept
1253 {
1254 auto inf_result = (a <= batch_type(0.)) && is_flint(a);
1255 batch_type x = select(inf_result, constants::nan<batch_type>(), a);
1256 batch_type q = abs(x);
1257 #ifndef XSIMD_NO_INFINITIES
1258 inf_result = (x == constants::infinity<batch_type>()) || inf_result;
1259 #endif
1260 auto ltza = a < batch_type(0.);
1261 batch_type r(0);
1262 batch_type r1 = other(q);
1263 if (any(ltza))
1264 {
1265 #ifdef __FAST_MATH__
1266 r = negative(q, r1);
1267 #else
1268 r = select(inf_result, constants::infinity<batch_type>(), negative(q, r1));
1269 #endif
1270 if (all(ltza))
1271 return r;
1272 }
1273 batch_type r2 = select(ltza, r, r1);
1274 #ifdef __FAST_MATH__
1275 return r2;
1276 #else
1277 return select(a == constants::minusinfinity<batch_type>(), constants::nan<batch_type>(), select(inf_result, constants::infinity<batch_type>(), r2));
1278 #endif
1279 }
1280
1281 private:
1282 static XSIMD_INLINE batch_type negative(const batch_type& q, const batch_type& w) noexcept
1283 {
1284 batch_type p = floor(q);
1285 batch_type z = q - p;
1286 auto test2 = z < batch_type(0.5);
1287 z = select(test2, z - batch_type(1.), z);
1288 z = q * sin(z, trigo_pi_tag());
1289 return -log(constants::invpi<batch_type>() * abs(z)) - w;
1290 }
1291
1292 static XSIMD_INLINE batch_type other(const batch_type& x) noexcept
1293 {
1294 auto xlt650 = (x < batch_type(6.5));
1295 batch_type r0x = x;
1296 batch_type r0z = x;
1297 batch_type r0s = batch_type(1.);
1298 batch_type r1 = batch_type(0.);
1299 batch_type p = constants::nan<batch_type>();
1300 if (any(xlt650))
1301 {
1302 batch_type z = batch_type(1.);
1303 batch_type tx = select(xlt650, x, batch_type(0.));
1304 batch_type nx = batch_type(0.);
1305 const batch_type _075 = batch_type(0.75);
1306 const batch_type _150 = batch_type(1.50);
1307 const batch_type _125 = batch_type(1.25);
1308 const batch_type _250 = batch_type(2.50);
1309 auto xge150 = (x >= _150);
1310 auto txgt250 = (tx > _250);
1311
1312
1313 while (any(xge150 && txgt250))
1314 {
1315 nx = select(txgt250, nx - batch_type(1.), nx);
1316 tx = select(txgt250, x + nx, tx);
1317 z = select(txgt250, z * tx, z);
1318 txgt250 = (tx > _250);
1319 }
1320 r0x = select(xge150, x + nx - batch_type(2.), x);
1321 r0z = select(xge150, z, r0z);
1322 r0s = select(xge150, batch_type(1.), r0s);
1323
1324
1325 auto xge125 = (x >= _125);
1326 auto xge125t = xge125 && !xge150;
1327 if (any(xge125))
1328 {
1329 r0x = select(xge125t, x - batch_type(1.), r0x);
1330 r0z = select(xge125t, z * x, r0z);
1331 r0s = select(xge125t, batch_type(-1.), r0s);
1332 }
1333
1334
1335 batch_bool<float, A> kernelC(false);
1336 auto xge075 = (x >= _075);
1337 auto xge075t = xge075 && !xge125;
1338 if (any(xge075t))
1339 {
1340 kernelC = xge075t;
1341 r0x = select(xge075t, x - batch_type(1.), x);
1342 r0z = select(xge075t, batch_type(1.), r0z);
1343 r0s = select(xge075t, batch_type(-1.), r0s);
1344 p = gammalnC(r0x);
1345 }
1346
1347
1348 auto txlt150 = (tx < _150) && !xge075;
1349 if (any(txlt150))
1350 {
1351 auto orig = txlt150;
1352 while (any(txlt150))
1353 {
1354 z = select(txlt150, z * tx, z);
1355 nx = select(txlt150, nx + batch_type(1.), nx);
1356 tx = select(txlt150, x + nx, tx);
1357 txlt150 = (tx < _150) && !xge075;
1358 }
1359 r0x = select(orig, r0x + nx - batch_type(2.), r0x);
1360 r0z = select(orig, z, r0z);
1361 r0s = select(orig, batch_type(-1.), r0s);
1362 }
1363 p = select(kernelC, p, gammalnB(r0x));
1364 if (all(xlt650))
1365 return fma(r0x, p, r0s * log(abs(r0z)));
1366 }
1367 r0z = select(xlt650, abs(r0z), x);
1368 batch_type m = log(r0z);
1369 r1 = fma(r0x, p, r0s * m);
1370 batch_type r2 = fma(x - batch_type(0.5), m, constants::logsqrt2pi<batch_type>() - x);
1371 r2 += gammaln2(batch_type(1.) / (x * x)) / x;
1372 return select(xlt650, r1, r2);
1373 }
1374 };
1375
1376 template <class A>
1377 struct lgamma_impl<batch<double, A>>
1378 {
1379 using batch_type = batch<double, A>;
1380
1381 static XSIMD_INLINE batch_type compute(const batch_type& a) noexcept
1382 {
1383 auto inf_result = (a <= batch_type(0.)) && is_flint(a);
1384 batch_type x = select(inf_result, constants::nan<batch_type>(), a);
1385 batch_type q = abs(x);
1386 #ifndef XSIMD_NO_INFINITIES
1387 inf_result = (q == constants::infinity<batch_type>());
1388 #endif
1389 auto test = (a < batch_type(-34.));
1390 batch_type r = constants::nan<batch_type>();
1391 if (any(test))
1392 {
1393 r = large_negative(q);
1394 if (all(test))
1395 return select(inf_result, constants::nan<batch_type>(), r);
1396 }
1397 batch_type r1 = other(a);
1398 batch_type r2 = select(test, r, r1);
1399 #ifdef __FAST_MATH__
1400 return r2;
1401 #else
1402 return select(a == constants::minusinfinity<batch_type>(), constants::nan<batch_type>(), select(inf_result, constants::infinity<batch_type>(), r2));
1403 #endif
1404 }
1405
1406 private:
1407
1408
1409 static inline batch_type large_negative(const batch_type& q) noexcept
1410 {
1411 batch_type w = lgamma(q);
1412 batch_type p = floor(q);
1413 batch_type z = q - p;
1414 auto test2 = (z < batch_type(0.5));
1415 z = select(test2, z - batch_type(1.), z);
1416 z = q * sin(z, trigo_pi_tag());
1417 z = abs(z);
1418 return constants::logpi<batch_type>() - log(z) - w;
1419 }
1420
1421 static XSIMD_INLINE batch_type other(const batch_type& xx) noexcept
1422 {
1423 batch_type x = xx;
1424 auto test = (x < batch_type(13.));
1425 batch_type r1 = batch_type(0.);
1426 if (any(test))
1427 {
1428 batch_type z = batch_type(1.);
1429 batch_type p = batch_type(0.);
1430 batch_type u = select(test, x, batch_type(0.));
1431 auto test1 = (u >= batch_type(3.));
1432 while (any(test1))
1433 {
1434 p = select(test1, p - batch_type(1.), p);
1435 u = select(test1, x + p, u);
1436 z = select(test1, z * u, z);
1437 test1 = (u >= batch_type(3.));
1438 }
1439
1440 auto test2 = (u < batch_type(2.));
1441 while (any(test2))
1442 {
1443 z = select(test2, z / u, z);
1444 p = select(test2, p + batch_type(1.), p);
1445 u = select(test2, x + p, u);
1446 test2 = (u < batch_type(2.));
1447 }
1448
1449 z = abs(z);
1450 x += p - batch_type(2.);
1451 r1 = x * gammaln1(x) + log(z);
1452 if (all(test))
1453 return r1;
1454 }
1455 batch_type r2 = fma(xx - batch_type(0.5), log(xx), constants::logsqrt2pi<batch_type>() - xx);
1456 batch_type p = batch_type(1.) / (xx * xx);
1457 r2 += gammalnA(p) / xx;
1458 return select(test, r1, r2);
1459 }
1460 };
1461 }
1462
1463 template <class A, class T>
1464 XSIMD_INLINE batch<T, A> lgamma(batch<T, A> const& self, requires_arch<common>) noexcept
1465 {
1466 return detail::lgamma_impl<batch<T, A>>::compute(self);
1467 }
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479 template <class A>
1480 XSIMD_INLINE batch<float, A> log(batch<float, A> const& self, requires_arch<common>) noexcept
1481 {
1482 using batch_type = batch<float, A>;
1483 using int_type = as_integer_t<float>;
1484 using i_type = batch<int_type, A>;
1485 batch_type x = self;
1486 i_type k(0);
1487 auto isnez = (self != batch_type(0.));
1488 #ifndef XSIMD_NO_DENORMALS
1489 auto test = (self < constants::smallestposval<batch_type>()) && isnez;
1490 if (any(test))
1491 {
1492 k = select(batch_bool_cast<int_type>(test), k - i_type(23), k);
1493 x = select(test, x * batch_type(8388608ul), x);
1494 }
1495 #endif
1496 i_type ix = ::xsimd::bitwise_cast<int_type>(x);
1497 ix += 0x3f800000 - 0x3f3504f3;
1498 k += (ix >> 23) - 0x7f;
1499 ix = (ix & i_type(0x007fffff)) + 0x3f3504f3;
1500 x = ::xsimd::bitwise_cast<float>(ix);
1501 batch_type f = --x;
1502 batch_type s = f / (batch_type(2.) + f);
1503 batch_type z = s * s;
1504 batch_type w = z * z;
1505 batch_type t1 = w * detail::horner<batch_type, 0x3eccce13, 0x3e789e26>(w);
1506 batch_type t2 = z * detail::horner<batch_type, 0x3f2aaaaa, 0x3e91e9ee>(w);
1507 batch_type R = t2 + t1;
1508 batch_type hfsq = batch_type(0.5) * f * f;
1509 batch_type dk = to_float(k);
1510 detail::reassociation_barrier(dk, "keep compensated k conversion before split log(2) scaling");
1511 batch_type r = fma(dk, constants::log_2hi<batch_type>(), fma(s, (hfsq + R), dk * constants::log_2lo<batch_type>()) - hfsq + f);
1512 #ifdef __FAST_MATH__
1513 return r;
1514 #else
1515 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1516 return select(!(self >= batch_type(0.)), constants::nan<batch_type>(), zz);
1517 #endif
1518 }
1519
1520 template <class A>
1521 XSIMD_INLINE batch<double, A> log(batch<double, A> const& self, requires_arch<common>) noexcept
1522 {
1523 using batch_type = batch<double, A>;
1524 using int_type = as_integer_t<double>;
1525 using i_type = batch<int_type, A>;
1526
1527 batch_type x = self;
1528 i_type hx = ::xsimd::bitwise_cast<int_type>(x) >> 32;
1529 i_type k(0);
1530 auto isnez = (self != batch_type(0.));
1531 #ifndef XSIMD_NO_DENORMALS
1532 auto test = (self < constants::smallestposval<batch_type>()) && isnez;
1533 if (any(test))
1534 {
1535 k = select(batch_bool_cast<int_type>(test), k - i_type(54), k);
1536 x = select(test, x * batch_type(18014398509481984ull), x);
1537 }
1538 #endif
1539 hx += 0x3ff00000 - 0x3fe6a09e;
1540 k += (hx >> 20) - 0x3ff;
1541 batch_type dk = to_float(k);
1542 detail::reassociation_barrier(dk, "keep compensated k conversion before split log(2) scaling");
1543 hx = (hx & i_type(0x000fffff)) + 0x3fe6a09e;
1544 x = ::xsimd::bitwise_cast<double>(hx << 32 | (i_type(0xffffffff) & ::xsimd::bitwise_cast<int_type>(x)));
1545
1546 batch_type f = --x;
1547 batch_type hfsq = batch_type(0.5) * f * f;
1548 batch_type s = f / (batch_type(2.) + f);
1549 batch_type z = s * s;
1550 batch_type w = z * z;
1551
1552 batch_type t1 = w * detail::horner<batch_type, 0x3fd999999997fa04ll, 0x3fcc71c51d8e78afll, 0x3fc39a09d078c69fll>(w);
1553 batch_type t2 = z * detail::horner<batch_type, 0x3fe5555555555593ll, 0x3fd2492494229359ll, 0x3fc7466496cb03dell, 0x3fc2f112df3e5244ll>(w);
1554 batch_type R = t2 + t1;
1555 batch_type r = fma(dk, constants::log_2hi<batch_type>(), fma(s, (hfsq + R), dk * constants::log_2lo<batch_type>()) - hfsq + f);
1556 #ifdef __FAST_MATH__
1557 return r;
1558 #else
1559 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1560 return select(!(self >= batch_type(0.)), constants::nan<batch_type>(), zz);
1561 #endif
1562 }
1563
1564 template <class A, class T>
1565 XSIMD_INLINE batch<std::complex<T>, A> log(const batch<std::complex<T>, A>& z, requires_arch<common>) noexcept
1566 {
1567 return batch<std::complex<T>, A>(log(abs(z)), atan2(z.imag(), z.real()));
1568 }
1569
1570
1571 template <class A>
1572 XSIMD_INLINE batch<float, A> log2(batch<float, A> const& self, requires_arch<common>) noexcept
1573 {
1574 using batch_type = batch<float, A>;
1575 using int_type = as_integer_t<float>;
1576 using i_type = batch<int_type, A>;
1577 batch_type x = self;
1578 i_type k(0);
1579 auto isnez = (self != batch_type(0.));
1580 #ifndef XSIMD_NO_DENORMALS
1581 auto test = (self < constants::smallestposval<batch_type>()) && isnez;
1582 if (any(test))
1583 {
1584 k = select(batch_bool_cast<int_type>(test), k - i_type(25), k);
1585 x = select(test, x * batch_type(33554432ul), x);
1586 }
1587 #endif
1588 i_type ix = ::xsimd::bitwise_cast<int_type>(x);
1589 ix += 0x3f800000 - 0x3f3504f3;
1590 k += (ix >> 23) - 0x7f;
1591 ix = (ix & i_type(0x007fffff)) + 0x3f3504f3;
1592 x = ::xsimd::bitwise_cast<float>(ix);
1593 batch_type f = --x;
1594 batch_type s = f / (batch_type(2.) + f);
1595 batch_type z = s * s;
1596 batch_type w = z * z;
1597 batch_type t1 = w * detail::horner<batch_type, 0x3eccce13, 0x3e789e26>(w);
1598 batch_type t2 = z * detail::horner<batch_type, 0x3f2aaaaa, 0x3e91e9ee>(w);
1599 batch_type R = t1 + t2;
1600 batch_type hfsq = batch_type(0.5) * f * f;
1601 batch_type dk = to_float(k);
1602 detail::reassociation_barrier(dk, "prevent distributing multiplies through compensated exponent conversion");
1603 batch_type r = fma(fms(s, hfsq + R, hfsq) + f, constants::invlog_2<batch_type>(), dk);
1604 #ifdef __FAST_MATH__
1605 return r;
1606 #else
1607 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1608 return select(!(self >= batch_type(0.)), constants::nan<batch_type>(), zz);
1609 #endif
1610 }
1611
1612 template <class A>
1613 XSIMD_INLINE batch<double, A> log2(batch<double, A> const& self, requires_arch<common>) noexcept
1614 {
1615 using batch_type = batch<double, A>;
1616 using int_type = as_integer_t<double>;
1617 using i_type = batch<int_type, A>;
1618 batch_type x = self;
1619 i_type hx = ::xsimd::bitwise_cast<int_type>(x) >> 32;
1620 i_type k(0);
1621 auto isnez = (self != batch_type(0.));
1622 #ifndef XSIMD_NO_DENORMALS
1623 auto test = (self < constants::smallestposval<batch_type>()) && isnez;
1624 if (any(test))
1625 {
1626 k = select(batch_bool_cast<typename i_type::value_type>(test), k - i_type(54), k);
1627 x = select(test, x * batch_type(18014398509481984ull), x);
1628 }
1629 #endif
1630 hx += 0x3ff00000 - 0x3fe6a09e;
1631 k += (hx >> 20) - 0x3ff;
1632 hx = (hx & i_type(0x000fffff)) + 0x3fe6a09e;
1633 x = ::xsimd::bitwise_cast<double>(hx << 32 | (i_type(0xffffffff) & ::xsimd::bitwise_cast<int_type>(x)));
1634 batch_type f = --x;
1635 batch_type s = f / (batch_type(2.) + f);
1636 batch_type z = s * s;
1637 batch_type w = z * z;
1638 batch_type t1 = w * detail::horner<batch_type, 0x3fd999999997fa04ll, 0x3fcc71c51d8e78afll, 0x3fc39a09d078c69fll>(w);
1639 batch_type t2 = z * detail::horner<batch_type, 0x3fe5555555555593ll, 0x3fd2492494229359ll, 0x3fc7466496cb03dell, 0x3fc2f112df3e5244ll>(w);
1640 batch_type R = t2 + t1;
1641 batch_type hfsq = batch_type(0.5) * f * f;
1642 batch_type hi = f - hfsq;
1643 hi = hi & ::xsimd::bitwise_cast<double>((constants::allbits<i_type>() << 32));
1644 batch_type lo = fma(s, hfsq + R, f - hi - hfsq);
1645 batch_type val_hi = hi * constants::invlog_2hi<batch_type>();
1646 batch_type val_lo = fma(lo + hi, constants::invlog_2lo<batch_type>(), lo * constants::invlog_2hi<batch_type>());
1647 batch_type dk = to_float(k);
1648 detail::reassociation_barrier(dk, "Kahan compensated log2 summation");
1649 batch_type w1 = dk + val_hi;
1650 detail::reassociation_barrier(w1, "Kahan compensated log2 summation");
1651 val_lo += (dk - w1) + val_hi;
1652 val_hi = w1;
1653 batch_type r = val_lo + val_hi;
1654 #ifdef __FAST_MATH__
1655 return r;
1656 #else
1657 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1658 return select(!(self >= batch_type(0.)), constants::nan<batch_type>(), zz);
1659 #endif
1660 }
1661
1662 namespace detail
1663 {
1664 template <class T, class A>
1665 XSIMD_INLINE batch<T, A> logN_complex_impl(const batch<T, A>& z, typename batch<T, A>::value_type base) noexcept
1666 {
1667 using batch_type = batch<T, A>;
1668 using rv_type = typename batch_type::value_type;
1669 return log(z) / batch_type(rv_type(base));
1670 }
1671 }
1672
1673 template <class A, class T>
1674 XSIMD_INLINE batch<std::complex<T>, A> log2(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
1675 {
1676 return detail::logN_complex_impl(self, std::log(2));
1677 }
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691 template <class A>
1692 XSIMD_INLINE batch<float, A> log10(batch<float, A> const& self, requires_arch<common>) noexcept
1693 {
1694 using batch_type = batch<float, A>;
1695 const batch_type
1696 ivln10hi(4.3432617188e-01f),
1697 ivln10lo(-3.1689971365e-05f),
1698 log10_2hi(3.0102920532e-01f),
1699 log10_2lo(7.9034151668e-07f);
1700 using int_type = as_integer_t<float>;
1701 using i_type = batch<int_type, A>;
1702 batch_type x = self;
1703 i_type k(0);
1704 auto isnez = (self != batch_type(0.));
1705 #ifndef XSIMD_NO_DENORMALS
1706 auto test = (self < constants::smallestposval<batch_type>()) && isnez;
1707 if (any(test))
1708 {
1709 k = select(batch_bool_cast<int_type>(test), k - i_type(25), k);
1710 x = select(test, x * batch_type(33554432ul), x);
1711 }
1712 #endif
1713 i_type ix = ::xsimd::bitwise_cast<int_type>(x);
1714 ix += 0x3f800000 - 0x3f3504f3;
1715 k += (ix >> 23) - 0x7f;
1716 ix = (ix & i_type(0x007fffff)) + 0x3f3504f3;
1717 x = ::xsimd::bitwise_cast<float>(ix);
1718 batch_type f = --x;
1719 batch_type s = f / (batch_type(2.) + f);
1720 batch_type z = s * s;
1721 batch_type w = z * z;
1722 batch_type t1 = w * detail::horner<batch_type, 0x3eccce13, 0x3e789e26>(w);
1723 batch_type t2 = z * detail::horner<batch_type, 0x3f2aaaaa, 0x3e91e9ee>(w);
1724 batch_type R = t2 + t1;
1725 batch_type dk = to_float(k);
1726 detail::reassociation_barrier(dk, "prevent distributing multiplies through compensated exponent conversion");
1727 batch_type hfsq = batch_type(0.5) * f * f;
1728 batch_type hibits = f - hfsq;
1729 hibits &= ::xsimd::bitwise_cast<float>(i_type(0xfffff000));
1730 batch_type lobits = fma(s, hfsq + R, f - hibits - hfsq);
1731 batch_type r = fma(dk, log10_2hi,
1732 fma(hibits, ivln10hi,
1733 fma(lobits, ivln10hi,
1734 fma(lobits + hibits, ivln10lo, dk * log10_2lo))));
1735 #ifndef XSIMD_NO_INFINITIES
1736 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1737 #else
1738 assert(all(isnez) && "Calling log10 on a batch with zero value while XSIMD_NO_INFINITIES is active");
1739 batch_type zz = r;
1740 #endif
1741 #ifndef XSIMD_NO_NANS
1742 return select(!(self >= batch_type(0.)), constants::nan<batch_type>(), zz);
1743 #else
1744 assert(all(self >= batch_type(0.)) && "Calling log10 on a batch with negative value while XSIMD_NO_NANS is active");
1745 return zz;
1746 #endif
1747 }
1748
1749 template <class A>
1750 XSIMD_INLINE batch<double, A> log10(batch<double, A> const& self, requires_arch<common>) noexcept
1751 {
1752 using batch_type = batch<double, A>;
1753 const batch_type
1754 ivln10hi(4.34294481878168880939e-01),
1755 ivln10lo(2.50829467116452752298e-11),
1756 log10_2hi(3.01029995663611771306e-01),
1757 log10_2lo(3.69423907715893078616e-13);
1758 using int_type = as_integer_t<double>;
1759 using i_type = batch<int_type, A>;
1760 batch_type x = self;
1761 i_type hx = ::xsimd::bitwise_cast<int_type>(x) >> 32;
1762 i_type k(0);
1763 auto isnez = (self != batch_type(0.));
1764 #ifndef XSIMD_NO_DENORMALS
1765 auto test = (self < constants::smallestposval<batch_type>()) && isnez;
1766 if (any(test))
1767 {
1768 k = select(batch_bool_cast<int_type>(test), k - i_type(54), k);
1769 x = select(test, x * batch_type(18014398509481984ull), x);
1770 }
1771 #endif
1772 hx += 0x3ff00000 - 0x3fe6a09e;
1773 k += (hx >> 20) - 0x3ff;
1774 batch_type dk = to_float(k);
1775 detail::reassociation_barrier(dk, "prevent distributing multiplies through compensated exponent conversion");
1776 hx = (hx & i_type(0x000fffff)) + 0x3fe6a09e;
1777 x = ::xsimd::bitwise_cast<double>(hx << 32 | (i_type(0xffffffff) & ::xsimd::bitwise_cast<int_type>(x)));
1778 batch_type f = --x;
1779 batch_type s = f / (batch_type(2.) + f);
1780 batch_type z = s * s;
1781 batch_type w = z * z;
1782 batch_type t1 = w * detail::horner<batch_type, 0x3fd999999997fa04ll, 0x3fcc71c51d8e78afll, 0x3fc39a09d078c69fll>(w);
1783 batch_type t2 = z * detail::horner<batch_type, 0x3fe5555555555593ll, 0x3fd2492494229359ll, 0x3fc7466496cb03dell, 0x3fc2f112df3e5244ll>(w);
1784 batch_type R = t2 + t1;
1785 batch_type hfsq = batch_type(0.5) * f * f;
1786 batch_type hi = f - hfsq;
1787 hi = hi & ::xsimd::bitwise_cast<double>(constants::allbits<i_type>() << 32);
1788 batch_type lo = f - hi - hfsq + s * (hfsq + R);
1789 batch_type val_hi = hi * ivln10hi;
1790 batch_type y = dk * log10_2hi;
1791 batch_type val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi;
1792 batch_type w1 = y + val_hi;
1793 val_lo += (y - w1) + val_hi;
1794 val_hi = w1;
1795 batch_type r = val_lo + val_hi;
1796 #ifdef __FAST_MATH__
1797 return r;
1798 #else
1799 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1800 return select(!(self >= batch_type(0.)), constants::nan<batch_type>(), zz);
1801 #endif
1802 }
1803
1804 template <class A, class T>
1805 XSIMD_INLINE batch<std::complex<T>, A> log10(const batch<std::complex<T>, A>& z, requires_arch<common>) noexcept
1806 {
1807 return detail::logN_complex_impl(z, std::log(10));
1808 }
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820 template <class A>
1821 XSIMD_INLINE batch<float, A> log1p(batch<float, A> const& self, requires_arch<common>) noexcept
1822 {
1823 using batch_type = batch<float, A>;
1824 using int_type = as_integer_t<float>;
1825 using i_type = batch<int_type, A>;
1826 const batch_type uf = self + batch_type(1.);
1827 auto isnez = (uf != batch_type(0.));
1828 i_type iu = ::xsimd::bitwise_cast<int_type>(uf);
1829 iu += 0x3f800000 - 0x3f3504f3;
1830 i_type k = (iu >> 23) - 0x7f;
1831 iu = (iu & i_type(0x007fffff)) + 0x3f3504f3;
1832 batch_type f = --(::xsimd::bitwise_cast<float>(iu));
1833 batch_type s = f / (batch_type(2.) + f);
1834 batch_type z = s * s;
1835 batch_type w = z * z;
1836 batch_type t1 = w * detail::horner<batch_type, 0x3eccce13, 0x3e789e26>(w);
1837 batch_type t2 = z * detail::horner<batch_type, 0x3f2aaaaa, 0x3e91e9ee>(w);
1838 batch_type R = t2 + t1;
1839 batch_type hfsq = batch_type(0.5) * f * f;
1840 batch_type dk = to_float(k);
1841 detail::reassociation_barrier(dk, "prevent distributing multiplies through compensated exponent conversion");
1842
1843 batch_type c = select(batch_bool_cast<float>(k >= i_type(2)), batch_type(1.) - (uf - self), self - (uf - batch_type(1.))) / uf;
1844 batch_type r = fma(dk, constants::log_2hi<batch_type>(), fma(s, (hfsq + R), dk * constants::log_2lo<batch_type>() + c) - hfsq + f);
1845 #ifdef __FAST_MATH__
1846 return r;
1847 #else
1848 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1849 return select(!(uf >= batch_type(0.)), constants::nan<batch_type>(), zz);
1850 #endif
1851 }
1852
1853 template <class A>
1854 XSIMD_INLINE batch<double, A> log1p(batch<double, A> const& self, requires_arch<common>) noexcept
1855 {
1856 using batch_type = batch<double, A>;
1857 using int_type = as_integer_t<double>;
1858 using i_type = batch<int_type, A>;
1859 const batch_type uf = self + batch_type(1.);
1860 auto isnez = (uf != batch_type(0.));
1861 i_type hu = ::xsimd::bitwise_cast<int_type>(uf) >> 32;
1862 hu += 0x3ff00000 - 0x3fe6a09e;
1863 i_type k = (hu >> 20) - 0x3ff;
1864
1865 batch_type c = select(batch_bool_cast<double>(k >= i_type(2)), batch_type(1.) - (uf - self), self - (uf - batch_type(1.))) / uf;
1866 hu = (hu & i_type(0x000fffff)) + 0x3fe6a09e;
1867 batch_type f = ::xsimd::bitwise_cast<double>((hu << 32) | (i_type(0xffffffff) & ::xsimd::bitwise_cast<int_type>(uf)));
1868 f = --f;
1869 batch_type hfsq = batch_type(0.5) * f * f;
1870 batch_type s = f / (batch_type(2.) + f);
1871 batch_type z = s * s;
1872 batch_type w = z * z;
1873 batch_type t1 = w * detail::horner<batch_type, 0x3fd999999997fa04ll, 0x3fcc71c51d8e78afll, 0x3fc39a09d078c69fll>(w);
1874 batch_type t2 = z * detail::horner<batch_type, 0x3fe5555555555593ll, 0x3fd2492494229359ll, 0x3fc7466496cb03dell, 0x3fc2f112df3e5244ll>(w);
1875 batch_type R = t2 + t1;
1876 batch_type dk = to_float(k);
1877 detail::reassociation_barrier(dk, "prevent distributing multiplies through compensated exponent conversion");
1878 batch_type r = fma(dk, constants::log_2hi<batch_type>(), fma(s, hfsq + R, dk * constants::log_2lo<batch_type>() + c) - hfsq + f);
1879 #ifdef __FAST_MATH__
1880 return r;
1881 #else
1882 batch_type zz = select(isnez, select(self == constants::infinity<batch_type>(), constants::infinity<batch_type>(), r), constants::minusinfinity<batch_type>());
1883 return select(!(uf >= batch_type(0.)), constants::nan<batch_type>(), zz);
1884 #endif
1885 }
1886
1887 template <class A, class T>
1888 XSIMD_INLINE batch<std::complex<T>, A> log1p(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
1889 {
1890 using batch_type = batch<std::complex<T>, A>;
1891 using real_batch = typename batch_type::real_batch;
1892 batch_type u = 1 + self;
1893 batch_type logu = log(u);
1894 return select(u == batch_type(1.),
1895 self,
1896 select(u.real() <= real_batch(0.),
1897 logu,
1898 logu * self / (u - batch_type(1.))));
1899 }
1900
1901
1902 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
1903 XSIMD_INLINE batch<T, A> mod(batch<T, A> const& self, batch<T, A> const& other, requires_arch<common>) noexcept
1904 {
1905 return detail::apply([](T x, T y) noexcept -> T
1906 { return x % y; },
1907 self, other);
1908 }
1909
1910
1911 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
1912 XSIMD_INLINE batch<T, A> nearbyint(batch<T, A> const& self, requires_arch<common>) noexcept
1913 {
1914 return self;
1915 }
1916 namespace detail
1917 {
1918 template <class A, class T>
1919 XSIMD_INLINE batch<T, A> nearbyintf(batch<T, A> const& self) noexcept
1920 {
1921 using batch_type = batch<T, A>;
1922 batch_type s = bitofsign(self);
1923 batch_type v = self ^ s;
1924 batch_type t2n = constants::twotonmb<batch_type>();
1925 batch_type d0 = v + t2n;
1926 detail::reassociation_barrier(d0, "prevent collapsing (v + 2^n) - 2^n back to v");
1927 batch_type d = d0 - t2n;
1928 return s ^ select(v < t2n, d, v);
1929 }
1930 }
1931 template <class A>
1932 XSIMD_INLINE batch<float, A> nearbyint(batch<float, A> const& self, requires_arch<common>) noexcept
1933 {
1934 return detail::nearbyintf(self);
1935 }
1936 template <class A>
1937 XSIMD_INLINE batch<double, A> nearbyint(batch<double, A> const& self, requires_arch<common>) noexcept
1938 {
1939 return detail::nearbyintf(self);
1940 }
1941
1942
1943 template <class T, class A, class = std::enable_if_t<std::is_integral<T>::value>>
1944 XSIMD_INLINE batch<T, A> nearbyint_as_int(batch<T, A> const& self, requires_arch<common>) noexcept
1945 {
1946 return self;
1947 }
1948
1949
1950 template <class A>
1951 XSIMD_INLINE batch<as_integer_t<float>, A>
1952 nearbyint_as_int(batch<float, A> const& self, requires_arch<common>) noexcept
1953 {
1954 using U = as_integer_t<float>;
1955 return kernel::detail::apply_transform<U>([](float x) noexcept -> U
1956 { return std::nearbyintf(x); },
1957 self);
1958 }
1959
1960 template <class A>
1961 XSIMD_INLINE batch<as_integer_t<double>, A>
1962 nearbyint_as_int(batch<double, A> const& self, requires_arch<common>) noexcept
1963 {
1964 using U = as_integer_t<double>;
1965 return kernel::detail::apply_transform<U>([](double x) noexcept -> U
1966 { return std::nearbyint(x); },
1967 self);
1968 }
1969
1970
1971 namespace detail
1972 {
1973 template <class T, class A, bool is_int = std::is_integral<T>::value>
1974 struct nextafter_kernel
1975 {
1976 using batch_type = batch<T, A>;
1977
1978 static XSIMD_INLINE batch_type next(batch_type const& b) noexcept
1979 {
1980 return b;
1981 }
1982
1983 static XSIMD_INLINE batch_type prev(batch_type const& b) noexcept
1984 {
1985 return b;
1986 }
1987 };
1988
1989 template <class T, class A>
1990 struct bitwise_cast_batch;
1991
1992 template <class A>
1993 struct bitwise_cast_batch<float, A>
1994 {
1995 using type = batch<int32_t, A>;
1996 };
1997
1998 template <class A>
1999 struct bitwise_cast_batch<double, A>
2000 {
2001 using type = batch<int64_t, A>;
2002 };
2003
2004 template <class T, class A>
2005 struct nextafter_kernel<T, A, false>
2006 {
2007 using batch_type = batch<T, A>;
2008 using int_batch = typename bitwise_cast_batch<T, A>::type;
2009 using int_type = typename int_batch::value_type;
2010
2011 static XSIMD_INLINE batch_type next(const batch_type& b) noexcept
2012 {
2013 batch_type n = ::xsimd::bitwise_cast<T>(::xsimd::bitwise_cast<int_type>(b) + int_type(1));
2014 #ifdef __FAST_MATH__
2015 return n;
2016 #else
2017 return select(b == constants::infinity<batch_type>(), b, n);
2018 #endif
2019 }
2020
2021 static XSIMD_INLINE batch_type prev(const batch_type& b) noexcept
2022 {
2023 batch_type p = ::xsimd::bitwise_cast<T>(::xsimd::bitwise_cast<int_type>(b) - int_type(1));
2024 #ifdef __FAST_MATH__
2025 return p;
2026 #else
2027 return select(b == constants::minusinfinity<batch_type>(), b, p);
2028 #endif
2029 }
2030 };
2031 }
2032 template <class A, class T>
2033 XSIMD_INLINE batch<T, A> nextafter(batch<T, A> const& from, batch<T, A> const& to, requires_arch<common>) noexcept
2034 {
2035 using kernel = detail::nextafter_kernel<T, A>;
2036 return select(from == to, from,
2037 select(to > from, kernel::next(from), kernel::prev(from)));
2038 }
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050 template <class A, class T>
2051 XSIMD_INLINE batch<T, A> pow(batch<T, A> const& self, batch<T, A> const& other, requires_arch<common>) noexcept
2052 {
2053 using batch_type = batch<T, A>;
2054 const auto zero = batch_type(0.);
2055 auto negself = self < zero;
2056 auto iszeropowpos = self == zero && other >= zero;
2057 auto adj_self = select(iszeropowpos, batch_type(1), abs(self));
2058 batch_type z = exp(other * log(adj_self));
2059 z = select(iszeropowpos, zero, z);
2060 z = select(is_odd(other) && negself, -z, z);
2061 auto invalid = negself && !(is_flint(other) || isinf(other));
2062 return select(invalid, constants::nan<batch_type>(), z);
2063 }
2064
2065 template <class A, class T>
2066 XSIMD_INLINE batch<std::complex<T>, A> pow(const batch<std::complex<T>, A>& a, const batch<std::complex<T>, A>& z, requires_arch<common>) noexcept
2067 {
2068 using cplx_batch = batch<std::complex<T>, A>;
2069 using real_batch = typename cplx_batch::real_batch;
2070 real_batch absa = abs(a);
2071 real_batch arga = arg(a);
2072 real_batch x = z.real();
2073 real_batch y = z.imag();
2074 real_batch r = pow(absa, x);
2075 real_batch theta = x * arga;
2076 real_batch ze(0);
2077 auto cond = (y == ze);
2078 r = select(cond, r, r * exp(-y * arga));
2079 theta = select(cond, theta, theta + y * log(absa));
2080 auto sincosTheta = xsimd::sincos(theta);
2081 return select(absa == ze, cplx_batch(ze), cplx_batch(r * sincosTheta.second, r * sincosTheta.first));
2082 }
2083
2084 template <class A, class T>
2085 inline batch<std::complex<T>, A> pow(const batch<std::complex<T>, A>& a, const batch<T, A>& z, requires_arch<common>) noexcept
2086 {
2087 using cplx_batch = batch<std::complex<T>, A>;
2088
2089 auto absa = abs(a);
2090 auto arga = arg(a);
2091 auto r = pow(absa, z);
2092
2093 auto theta = z * arga;
2094 auto sincosTheta = xsimd::sincos(theta);
2095 return select(absa == 0, cplx_batch(0), cplx_batch(r * sincosTheta.second, r * sincosTheta.first));
2096 }
2097
2098 template <class A, class T>
2099 inline batch<std::complex<T>, A> pow(const batch<T, A>& a, const batch<std::complex<T>, A>& z, requires_arch<common>) noexcept
2100 {
2101 return pow(batch<std::complex<T>, A> { a, batch<T, A> {} }, z);
2102 }
2103
2104
2105 template <class T, class A, class = std::enable_if_t<std::is_floating_point<T>::value>>
2106 XSIMD_INLINE batch<T, A> reciprocal(batch<T, A> const& self,
2107 requires_arch<common>) noexcept
2108 {
2109 using batch_type = batch<T, A>;
2110 return div(batch_type(1), self);
2111 }
2112
2113
2114 template <class A, class T>
2115 XSIMD_INLINE std::complex<T> reduce_add(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
2116 {
2117 return { reduce_add(self.real()), reduce_add(self.imag()) };
2118 }
2119
2120 template <class A, class T, class >
2121 XSIMD_INLINE T reduce_add(batch<T, A> const& self, requires_arch<common>) noexcept
2122 {
2123 alignas(A::alignment()) T buffer[batch<T, A>::size];
2124 self.store_aligned(buffer);
2125 T res = 0;
2126 for (T val : buffer)
2127 {
2128 res += val;
2129 }
2130 return res;
2131 }
2132
2133 namespace detail
2134 {
2135 template <class T, T N>
2136 struct split_high
2137 {
2138 static constexpr T get(T i, T)
2139 {
2140 return i < N ? (i + N) : ((i % N) + N);
2141 }
2142 };
2143
2144 template <class Op, class A, class T>
2145 XSIMD_INLINE T reduce(Op, batch<T, A> const& self, std::integral_constant<unsigned, 1>) noexcept
2146 {
2147 return ::xsimd::kernel::first(self, A {});
2148 }
2149
2150 template <class Op, class A, class T, unsigned Lvl>
2151 XSIMD_INLINE T reduce(Op op, batch<T, A> const& self, std::integral_constant<unsigned, Lvl>) noexcept
2152 {
2153 using index_type = as_unsigned_integer_t<T>;
2154 batch<T, A> split = swizzle(self, make_batch_constant<index_type, split_high<index_type, Lvl / 2>, A>());
2155 return reduce(op, op(split, self), std::integral_constant<unsigned, Lvl / 2>());
2156 }
2157 }
2158
2159
2160 template <class A, class T>
2161 XSIMD_INLINE T reduce_max(batch<T, A> const& self, requires_arch<common>) noexcept
2162 {
2163 return detail::reduce([](batch<T, A> const& x, batch<T, A> const& y)
2164 { return max(x, y); },
2165 self, std::integral_constant<unsigned, batch<T, A>::size>());
2166 }
2167
2168
2169 template <class A, class T>
2170 XSIMD_INLINE T reduce_min(batch<T, A> const& self, requires_arch<common>) noexcept
2171 {
2172 return detail::reduce([](batch<T, A> const& x, batch<T, A> const& y)
2173 { return min(x, y); },
2174 self, std::integral_constant<unsigned, batch<T, A>::size>());
2175 }
2176
2177
2178 template <class A, class T>
2179 XSIMD_INLINE std::complex<T> reduce_mul(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
2180 {
2181
2182 alignas(A::alignment()) std::complex<T> buffer[batch<std::complex<T>, A>::size];
2183 self.store_aligned(buffer);
2184 std::complex<T> res = 1;
2185 for (auto val : buffer)
2186 {
2187 res *= val;
2188 }
2189 return res;
2190 }
2191
2192 template <class A, class T, class >
2193 XSIMD_INLINE T reduce_mul(batch<T, A> const& self, requires_arch<common>) noexcept
2194 {
2195 alignas(A::alignment()) T buffer[batch<T, A>::size];
2196 self.store_aligned(buffer);
2197 T res = 1;
2198 for (T val : buffer)
2199 {
2200 res *= val;
2201 }
2202 return res;
2203 }
2204
2205
2206 template <class A>
2207 XSIMD_INLINE batch<float, A> remainder(batch<float, A> const& self, batch<float, A> const& other, requires_arch<common>) noexcept
2208 {
2209 batch<float, A> q = nearbyint(self / other);
2210 detail::reassociation_barrier(q, "prevent pulling multiply back through rounded quotient");
2211 return fnma(q, other, self);
2212 }
2213 template <class A>
2214 XSIMD_INLINE batch<double, A> remainder(batch<double, A> const& self, batch<double, A> const& other, requires_arch<common>) noexcept
2215 {
2216 batch<double, A> q = nearbyint(self / other);
2217 detail::reassociation_barrier(q, "prevent pulling multiply back through rounded quotient");
2218 return fnma(q, other, self);
2219 }
2220 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
2221 XSIMD_INLINE batch<T, A> remainder(batch<T, A> const& self, batch<T, A> const& other, requires_arch<common>) noexcept
2222 {
2223 auto mod = self % other;
2224 return select(mod <= other / 2, mod, mod - other);
2225 }
2226
2227
2228 template <class A, class T>
2229 XSIMD_INLINE batch<std::complex<T>, A> select(batch_bool<T, A> const& cond, batch<std::complex<T>, A> const& true_br, batch<std::complex<T>, A> const& false_br, requires_arch<common>) noexcept
2230 {
2231 return { select(cond, true_br.real(), false_br.real()), select(cond, true_br.imag(), false_br.imag()) };
2232 }
2233
2234
2235 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
2236 XSIMD_INLINE batch<T, A> sign(batch<T, A> const& self, requires_arch<common>) noexcept
2237 {
2238 using batch_type = batch<T, A>;
2239 batch_type res = select(self > batch_type(0), batch_type(1), batch_type(0)) - select(self < batch_type(0), batch_type(1), batch_type(0));
2240 return res;
2241 }
2242
2243 namespace detail
2244 {
2245 template <class T, class A>
2246 XSIMD_INLINE batch<T, A> signf(batch<T, A> const& self) noexcept
2247 {
2248 using batch_type = batch<T, A>;
2249 batch_type res = select(self > batch_type(0.f), batch_type(1.f), batch_type(0.f)) - select(self < batch_type(0.f), batch_type(1.f), batch_type(0.f));
2250 #ifdef XSIMD_NO_NANS
2251 return res;
2252 #else
2253 return select(isnan(self), constants::nan<batch_type>(), res);
2254 #endif
2255 }
2256 }
2257
2258 template <class A>
2259 XSIMD_INLINE batch<float, A> sign(batch<float, A> const& self, requires_arch<common>) noexcept
2260 {
2261 return detail::signf(self);
2262 }
2263 template <class A>
2264 XSIMD_INLINE batch<double, A> sign(batch<double, A> const& self, requires_arch<common>) noexcept
2265 {
2266 return detail::signf(self);
2267 }
2268 template <class A, class T>
2269 XSIMD_INLINE batch<std::complex<T>, A> sign(const batch<std::complex<T>, A>& z, requires_arch<common>) noexcept
2270 {
2271 using batch_type = batch<std::complex<T>, A>;
2272 using real_batch = typename batch_type::real_batch;
2273 auto rz = z.real();
2274 auto iz = z.imag();
2275 return select(rz != real_batch(0.),
2276 batch_type(sign(rz)),
2277 batch_type(sign(iz)));
2278 }
2279
2280
2281 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
2282 XSIMD_INLINE batch<T, A> signnz(batch<T, A> const& self, requires_arch<common>) noexcept
2283 {
2284 using batch_type = batch<T, A>;
2285 return (self >> (sizeof(T) * 8 - 1)) | batch_type(1.);
2286 }
2287
2288 namespace detail
2289 {
2290 template <class T, class A>
2291 XSIMD_INLINE batch<T, A> signnzf(batch<T, A> const& self) noexcept
2292 {
2293 using batch_type = batch<T, A>;
2294 #ifndef XSIMD_NO_NANS
2295 return select(isnan(self), constants::nan<batch_type>(), batch_type(1.) | (constants::signmask<batch_type>() & self));
2296 #else
2297 return batch_type(1.) | (constants::signmask<batch_type>() & self);
2298 #endif
2299 }
2300 }
2301
2302 template <class A>
2303 XSIMD_INLINE batch<float, A> signnz(batch<float, A> const& self, requires_arch<common>) noexcept
2304 {
2305 return detail::signnzf(self);
2306 }
2307 template <class A>
2308 XSIMD_INLINE batch<double, A> signnz(batch<double, A> const& self, requires_arch<common>) noexcept
2309 {
2310 return detail::signnzf(self);
2311 }
2312
2313
2314 template <class A, class T>
2315 XSIMD_INLINE batch<std::complex<T>, A> sqrt(batch<std::complex<T>, A> const& z, requires_arch<common>) noexcept
2316 {
2317
2318 constexpr T csqrt_scale_factor = std::is_same<T, float>::value ? 6.7108864e7f : 1.8014398509481984e16;
2319 constexpr T csqrt_scale = std::is_same<T, float>::value ? 1.220703125e-4f : 7.450580596923828125e-9;
2320 using batch_type = batch<std::complex<T>, A>;
2321 using real_batch = batch<T, A>;
2322 real_batch x = z.real();
2323 real_batch y = z.imag();
2324 real_batch sqrt_x = sqrt(fabs(x));
2325 real_batch sqrt_hy = sqrt(0.5 * fabs(y));
2326 auto cond = (fabs(x) > real_batch(4.) || fabs(y) > real_batch(4.));
2327 x = select(cond, x * 0.25, x * csqrt_scale_factor);
2328 y = select(cond, y * 0.25, y * csqrt_scale_factor);
2329 real_batch scale = select(cond, real_batch(2.), real_batch(csqrt_scale));
2330 real_batch r = abs(batch_type(x, y));
2331
2332 auto condxp = x > real_batch(0.);
2333 real_batch t0 = select(condxp, xsimd::sqrt(0.5 * (r + x)), xsimd::sqrt(0.5 * (r - x)));
2334 real_batch r0 = scale * fabs((0.5 * y) / t0);
2335 t0 *= scale;
2336 real_batch t = select(condxp, t0, r0);
2337 r = select(condxp, r0, t0);
2338 batch_type resg = select(y < real_batch(0.), batch_type(t, -r), batch_type(t, r));
2339 real_batch ze(0.);
2340
2341 return select(y == ze,
2342 select(x == ze,
2343 batch_type(ze, ze),
2344 select(x < ze, batch_type(ze, sqrt_x), batch_type(sqrt_x, ze))),
2345 select(x == ze,
2346 select(y > ze, batch_type(sqrt_hy, sqrt_hy), batch_type(sqrt_hy, -sqrt_hy)),
2347 resg));
2348 }
2349
2350
2351
2352 namespace detail
2353 {
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363 template <class B>
2364 struct stirling_kernel;
2365
2366 template <class A>
2367 struct stirling_kernel<batch<float, A>>
2368 {
2369 using batch_type = batch<float, A>;
2370 static XSIMD_INLINE batch_type compute(const batch_type& x) noexcept
2371 {
2372 return horner<batch_type,
2373 0x3daaaaab,
2374 0x3b638e39,
2375 0xbb2fb930,
2376 0xb970b359>(x);
2377 }
2378
2379 static XSIMD_INLINE batch_type split_limit() noexcept
2380 {
2381 return batch_type(bit_cast<float>(uint32_t(0x41d628f6)));
2382 }
2383
2384 static XSIMD_INLINE batch_type large_limit() noexcept
2385 {
2386 return batch_type(bit_cast<float>(uint32_t(0x420c28f3)));
2387 }
2388 };
2389
2390 template <class A>
2391 struct stirling_kernel<batch<double, A>>
2392 {
2393 using batch_type = batch<double, A>;
2394 static XSIMD_INLINE batch_type compute(const batch_type& x) noexcept
2395 {
2396 return horner<batch_type,
2397 0x3fb5555555555986ull,
2398 0x3f6c71c71b98c5fdull,
2399 0xbf65f72607d44fd7ull,
2400 0xbf2e166b27e61d7cull,
2401 0x3f49cc72592d7293ull
2402 >(x);
2403 }
2404
2405 static XSIMD_INLINE batch_type split_limit() noexcept
2406 {
2407 return batch_type(bit_cast<double>(uint64_t(0x4061e083ba3443d4)));
2408 }
2409
2410 static XSIMD_INLINE batch_type large_limit() noexcept
2411 {
2412 return batch_type(bit_cast<double>(uint64_t(0x4065800000000000)));
2413 }
2414 };
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425 template <class T, class A>
2426 XSIMD_INLINE batch<T, A> stirling(const batch<T, A>& a) noexcept
2427 {
2428 using batch_type = batch<T, A>;
2429 const batch_type stirlingsplitlim = stirling_kernel<batch_type>::split_limit();
2430 const batch_type stirlinglargelim = stirling_kernel<batch_type>::large_limit();
2431 batch_type x = select(a >= batch_type(0.), a, constants::nan<batch_type>());
2432 batch_type w = batch_type(1.) / x;
2433 w = fma(w, stirling_kernel<batch_type>::compute(w), batch_type(1.));
2434 batch_type y = exp(-x);
2435 auto test = (x < stirlingsplitlim);
2436 batch_type z = x - batch_type(0.5);
2437 z = select(test, z, batch_type(0.5) * z);
2438 batch_type v = exp(z * log(abs(x)));
2439 y *= v;
2440 y = select(test, y, y * v);
2441 y *= constants::sqrt_2pi<batch_type>() * w;
2442 #ifdef __FAST_MATH__
2443 return y;
2444 #else
2445 y = select(isinf(x), x, y);
2446 return select(x > stirlinglargelim, constants::infinity<batch_type>(), y);
2447 #endif
2448 }
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459 template <class B>
2460 struct tgamma_kernel;
2461
2462 template <class A>
2463 struct tgamma_kernel<batch<float, A>>
2464 {
2465 using batch_type = batch<float, A>;
2466 static XSIMD_INLINE batch_type compute(const batch_type& x) noexcept
2467 {
2468 return horner<batch_type,
2469 0x3f800000UL,
2470 0x3ed87799UL,
2471 0x3ed2d411UL,
2472 0x3da82a34UL,
2473 0x3d93ae7cUL,
2474 0x3b91db14UL,
2475 0x3ba90c99UL,
2476 0x3ad28b22UL
2477 >(x);
2478 }
2479 };
2480
2481 template <class A>
2482 struct tgamma_kernel<batch<double, A>>
2483 {
2484 using batch_type = batch<double, A>;
2485 static XSIMD_INLINE batch_type compute(const batch_type& x) noexcept
2486 {
2487 return horner<batch_type,
2488 0x3ff0000000000000ULL,
2489 0x3fdfa1373993e312ULL,
2490 0x3fca8da9dcae7d31ULL,
2491 0x3fa863d918c423d3ULL,
2492 0x3f8557cde9db14b0ULL,
2493 0x3f5384e3e686bfabULL,
2494 0x3f24fcb839982153ULL
2495 >(x)
2496 / horner<batch_type,
2497 0x3ff0000000000000ULL,
2498 0x3fb24944c9cd3c51ULL,
2499 0xbfce071a9d4287c2ULL,
2500 0x3fa25779e33fde67ULL,
2501 0x3f8831ed5b1bb117ULL,
2502 0xBf7240e4e750b44aULL,
2503 0x3f41ae8a29152573ULL,
2504 0xbef8487a8400d3aFULL
2505 >(x);
2506 }
2507 };
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518 template <class B>
2519 XSIMD_INLINE B tgamma_large_negative(const B& a) noexcept
2520 {
2521 B st = stirling(a);
2522 B p = floor(a);
2523 B sgngam = select(is_even(p), -B(1.), B(1.));
2524 B z = a - p;
2525 auto test2 = z < B(0.5);
2526 z = select(test2, z - B(1.), z);
2527 z = a * sin(z, trigo_pi_tag());
2528 z = abs(z);
2529 return sgngam * constants::pi<B>() / (z * st);
2530 }
2531
2532 template <class B, class BB>
2533 XSIMD_INLINE B tgamma_other(const B& a, const BB& test) noexcept
2534 {
2535 B x = select(test, B(2.), a);
2536 #ifndef XSIMD_NO_INFINITIES
2537 auto inf_result = (a == constants::infinity<B>());
2538 x = select(inf_result, B(2.), x);
2539 #endif
2540 B z = B(1.);
2541 auto test1 = (x >= B(3.));
2542 while (any(test1))
2543 {
2544 x = select(test1, x - B(1.), x);
2545 z = select(test1, z * x, z);
2546 test1 = (x >= B(3.));
2547 }
2548 test1 = (x < B(0.));
2549 while (any(test1))
2550 {
2551 z = select(test1, z / x, z);
2552 x = select(test1, x + B(1.), x);
2553 test1 = (x < B(0.));
2554 }
2555 auto test2 = (x < B(2.));
2556 while (any(test2))
2557 {
2558 z = select(test2, z / x, z);
2559 x = select(test2, x + B(1.), x);
2560 test2 = (x < B(2.));
2561 }
2562 x = z * tgamma_kernel<B>::compute(x - B(2.));
2563 #ifndef XSIMD_NO_INFINITIES
2564 return select(inf_result, a, x);
2565 #else
2566 return x;
2567 #endif
2568 }
2569 }
2570
2571 template <class A, class T>
2572 XSIMD_INLINE batch<T, A> tgamma(batch<T, A> const& self, requires_arch<common>) noexcept
2573 {
2574 using batch_type = batch<T, A>;
2575 auto nan_result = (self < batch_type(0.) && is_flint(self));
2576 #ifndef XSIMD_NO_NANS
2577 nan_result = isnan(self) || nan_result;
2578 #endif
2579 batch_type q = abs(self);
2580 auto test = (self < batch_type(-33.));
2581 batch_type r = constants::nan<batch_type>();
2582 if (any(test))
2583 {
2584 r = detail::tgamma_large_negative(q);
2585 if (all(test))
2586 return select(nan_result, constants::nan<batch_type>(), r);
2587 }
2588 batch_type r1 = detail::tgamma_other(self, test);
2589 batch_type r2 = select(test, r, r1);
2590 #ifdef __FAST_MATH__
2591 return r2;
2592 #else
2593 return select(self == batch_type(0.), copysign(constants::infinity<batch_type>(), self), select(nan_result, constants::nan<batch_type>(), r2));
2594 #endif
2595 }
2596
2597 }
2598
2599 }
2600
2601 #endif