use crate::{divider::Divider, math::policy::policies::MediumPrecision, vector::ops::AddMasked as _};
use core::f32::consts::{FRAC_1_PI, FRAC_PI_2, LN_10, LOG2_E, SQRT_2};
use super::*;
impl<V: FloatVectorWithBits<Element = f32>> SpecializedCoreMath<f32> for V {
#[inline(always)]
fn inverse_sqrt<P: Policy>(self) -> Self {
super::generic::inverse_sqrt_internal::<V, f32, P>(self)
}
}
#[rustfmt::skip]
impl<V: FloatVectorWithBits<Element = f32>> SpecializedSpatialMath<f32> for V {
#[inline(always)] fn l2_norm_squared<P: Policy>(self) -> Self { self * self }
#[inline(always)] fn l2_norm<P: Policy>(self) -> Self { self.abs() }
#[inline(always)] fn l1_norm<P: Policy>(self) -> Self { self.abs() }
}
impl<V: FloatVectorWithBits<Element = f32>> SpecializedTranscendentalMath<f32> for V {
#[inline(always)]
fn sinc<P: Policy>(self) -> Self {
super::generic::sinc_internal::<V, f32, P>(self)
}
#[inline(always)]
fn sinc_pi<P: Policy>(self) -> Self {
super::generic::sinc_pi_internal::<V, f32, P>(self)
}
#[inline(always)]
fn log_n<P: Policy, const N: usize>(self) -> Self {
super::generic::log_n_internal::<V, f32, P, N>(self)
}
#[inline(always)]
fn sin_cos<P: Policy>(self) -> (Self, Self) {
if const {
P::POLICY.precision.le(PrecisionPolicy::Average)
&& Self::NATIVE_CAP.has(NativeCapability::SIN | NativeCapability::COS)
} {
return unsafe { self.native_sin_cos::<P>() };
}
sin_cos_f_internal::<P, V, false, false>(self)
}
#[inline(always)]
fn sin<P: Policy>(self) -> Self {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && Self::NATIVE_CAP.has(NativeCapability::SIN) } {
return unsafe { self.native_sin::<P>() };
}
sin_cos_f_internal::<P, V, false, true>(self).0
}
#[inline(always)]
fn cos<P: Policy>(self) -> Self {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && Self::NATIVE_CAP.has(NativeCapability::COS) } {
return unsafe { self.native_cos::<P>() };
}
sin_cos_f_internal::<P, V, false, true>(self).1
}
#[inline(always)]
fn tan<P: Policy>(self) -> Self {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && Self::NATIVE_CAP.has(NativeCapability::TAN) } {
return unsafe { self.native_tan::<P>() };
}
let d = self;
let xa = d.abs().flush_denormals::<P>();
let (mut x, mut x_lo, q) = trig_range_reduction::<P, V, false>(xa);
let odd_sign = V::from_bits(q.shli::<31>());
x ^= odd_sign;
x_lo ^= odd_sign;
let x2 = x * x;
let mut x0 = x;
if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
x0 += x_lo;
}
#[rustfmt::skip]
let mut r = x2.poly_rev_p::<P, _>(&[
9.38540185543E-3, 3.11992232697E-3, 2.44301354525E-2, 5.34112807005E-2, 1.33387994085E-1, 3.33331568548E-1, ]).mul_adde(x2 * x, x0);
let odd = (q & V::Bits::ONE).cmp_ne(V::Bits::ZERO);
if const { P::POLICY.avoid_branching } || odd.any() {
r = odd.select(r.reciprocal_p::<P>(), r);
}
r = r.mul_sign(d);
if const { P::POLICY.check_overflow } {
r = d.is_finite().select(r, V::NAN);
}
r
}
#[inline(always)]
fn sincos_pi<P: Policy>(self) -> (Self, Self) {
if const {
P::POLICY.precision.le(PrecisionPolicy::Average)
&& Self::NATIVE_CAP.has(NativeCapability::SIN | NativeCapability::COS)
} {
return unsafe { (self * Self::PI).native_sin_cos::<P>() };
}
sin_cos_f_internal::<P, V, true, false>(self)
}
#[inline(always)]
fn sin_pi<P: Policy>(self) -> Self {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && Self::NATIVE_CAP.has(NativeCapability::SIN) } {
return unsafe { (self * Self::PI).native_sin::<P>() };
}
sin_cos_f_internal::<P, V, true, true>(self).0
}
#[inline(always)]
fn cos_pi<P: Policy>(self) -> Self {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && Self::NATIVE_CAP.has(NativeCapability::COS) } {
return unsafe { (self * Self::PI).native_cos::<P>() };
}
sin_cos_f_internal::<P, V, true, true>(self).1
}
#[inline(always)]
fn tan_pi<P: Policy>(self) -> Self {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && Self::NATIVE_CAP.has(NativeCapability::TAN) } {
return unsafe { (self * Self::PI).native_tan::<P>() };
}
let (s, c) = self.sincos_pi::<P>();
s / c
}
#[inline(always)]
fn sinh_cosh<P: Policy>(self) -> (Self, Self) {
let x0 = self;
let x = x0.abs().flush_denormals::<P>();
let y = x.exph_p::<P>();
let qy = V::FRAC_1_4 / y;
let mut sinh = y - qy;
let cosh = y + qy;
let x_small = x.cmp_lt(V::ONE);
if const { P::POLICY.precision.ge(PrecisionPolicy::Average) } && (P::POLICY.avoid_branching || x_small.any()) {
let x2 = x * x;
let y1 = x2
.poly_rev_p::<P, _>(&[2.03721912945E-4, 8.33028376239E-3, 1.66667160211E-1])
.mul_adde(x2 * x, x);
sinh = x_small.select(y1, sinh);
}
(sinh.mul_sign(x0), cosh)
}
#[inline(always)]
fn sinh<P: Policy>(self) -> Self {
let x0 = self;
let x = x0.abs().flush_denormals::<P>();
let x_small = x.cmp_lt(V::ONE);
let mut y2 = V::EMPTY;
if const { P::POLICY.avoid_branching || P::POLICY.precision.lt(PrecisionPolicy::Average) } || !x_small.all() {
y2 = x.exph_p::<P>();
y2 -= V::FRAC_1_4 / y2;
if const { P::POLICY.avoid_precision_branches() } {
return y2.mul_sign(x0);
}
}
if const { P::POLICY.precision.ge(PrecisionPolicy::Average) } && (P::POLICY.avoid_branching || x_small.any()) {
let x2 = x * x;
let y1 = x2
.poly_rev_p::<P, _>(&[2.03721912945E-4, 8.33028376239E-3, 1.66667160211E-1])
.mul_adde(x2 * x, x);
y2 = x_small.select(y1, y2);
}
y2.mul_sign(x0)
}
#[inline(always)]
fn cosh<P: Policy>(self) -> Self {
let y = self.abs().exph_p::<P>();
y + V::FRAC_1_4 / y
}
#[inline(always)]
#[rustfmt::skip]
fn tanh<P: Policy>(self) -> Self {
let x0 = self;
let one = V::ONE;
let x = x0.abs().flush_denormals::<P>();
let x_small = x.cmp_lt(crate::const_splat!(f32: 0.625));
let mut y2 = V::EMPTY;
if const { P::POLICY.avoid_branching || P::POLICY.precision.lt(PrecisionPolicy::Average) } || !x_small.all() {
let h = (x + x).exph_p::<P>();
y2 = (h - V::HALF) / (h + V::HALF);
if const { P::POLICY.check_overflow } {
y2 = x.cmp_gt(crate::const_splat!(f32: 44.4)).select(one, y2);
}
if const { P::POLICY.avoid_precision_branches() } {
return y2.mul_sign(x0);
}
}
if const { P::POLICY.precision.ge(PrecisionPolicy::Average) } && (P::POLICY.avoid_branching || x_small.any()) {
let x2 = x * x;
let y1 = x2.poly_rev_p::<P, _>(&[
-5.70498872745E-3,
2.06390887954E-2,
-5.37397155531E-2,
1.33314422036E-1,
-3.33332819422E-1,
]).mul_adde(x2 * x, x);
y2 = x_small.select(y1, y2);
}
y2.mul_sign(x0)
}
#[inline(always)]
fn asin<P: Policy>(self) -> Self {
asin_f_internal::<P, Self, false>(self)
}
#[inline(always)]
fn acos<P: Policy>(self) -> Self {
asin_f_internal::<P, Self, true>(self)
}
#[inline(always)]
fn atan<P: Policy>(self) -> Self {
let x = self;
let t = x.abs().flush_denormals::<P>();
if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
let a = t;
let gt1 = a.cmp_gt(V::ONE);
let s = gt1.select(a.reciprocal_p::<ExtraPrecision<P>>().flush_denormals::<P>(), a);
let t = s * s;
let r = t.mul_adde(s * crate::const_splat!(f32: 0.43157974), s)
/ t.mul_adde(
crate::const_splat!(f32: 0.05831938),
crate::const_splat!(f32: 0.76443945),
)
.mul_adde(t, V::ONE);
let r = gt1.select(V::FRAC_PI_2 - r, r);
return r.copysign(x);
}
let not_small = t.cmp_ge(crate::const_splat!(f32: SQRT_2 - 1.0)); let not_big = t.cmp_le(crate::const_splat!(f32: SQRT_2 + 1.0));
let s = not_big.select(V::FRAC_PI_4, V::FRAC_PI_2);
let a = V::NEG_ONE.zz(not_small).add_c(not_big, t);
let b = V::ONE.zz(not_big).add_c(not_small, t);
let z = a / b;
let z2 = z * z;
z2.poly_rev_p::<P, _>(&[8.05374449538E-2, -1.38776856032E-1, 1.99777106478E-1, -3.33329491539E-1])
.mul_adde(z2 * z, z.add_c(not_small, s)) .mul_sign(x)
}
#[inline(always)]
fn asinh<P: Policy>(self) -> Self {
let x0 = self;
let x = x0.abs().flush_denormals::<P>();
let x2 = x * x;
let x_small = x.cmp_le(crate::const_splat!(f32: 0.51));
let mut y2 = V::EMPTY;
if const { P::POLICY.avoid_branching } || !x_small.all() {
let x21 = if const { V::HAS_TRUE_FMA } {
x.mul_add(x, V::ONE)
} else {
x2 + V::ONE
};
y2 = (x21.sqrt() + x).ln_p::<P>();
if const { P::POLICY.check_overflow } {
let x_huge = x.cmp_gt(crate::const_splat!(f32: 1e10));
if const { P::POLICY.avoid_precision_branches() } || crate::unlikely(x_huge.any()) {
y2 = x_huge.select(x.ln_p::<P>() + V::LN_2, y2);
}
}
if const { P::POLICY.avoid_precision_branches() } {
return y2.mul_sign(x0);
}
}
if const { P::POLICY.avoid_branching } || x_small.any() {
let y1 = x2
.poly_rev_p::<P, _>(&[2.0122003309E-2, -4.2699340972E-2, 7.4847586088E-2, -1.6666288134E-1])
.mul_adde(x2 * x, x);
y2 = x_small.select(y1, y2);
}
y2.mul_sign(x0)
}
#[inline(always)]
fn acosh<P: Policy>(self) -> Self {
let x0 = self.flush_denormals::<P>();
let x1 = x0 - V::ONE;
let x_small = x1.cmp_lt(crate::const_splat!(f32: 0.49));
let mut y2 = V::EMPTY;
if const { P::POLICY.avoid_branching } || !x_small.all() {
y2 = (x0.mul_sube(x0, V::ONE).sqrt() + x0).ln_p::<P>();
if const { P::POLICY.check_overflow } {
let x_huge = x1.cmp_gt(crate::const_splat!(f32: 1e10));
if const { P::POLICY.avoid_precision_branches() } || crate::unlikely(x_huge.any()) {
y2 = x_huge.select(x0.ln_p::<P>() + V::LN_2, y2);
}
}
if const { P::POLICY.avoid_precision_branches() } {
return y2;
}
}
if const { P::POLICY.avoid_branching } || x_small.any() {
#[rustfmt::skip]
let mut y1 = x1.sqrt() * x1.poly_rev_p::<P, _>(&[
1.7596881071E-3,
-7.5272886713E-3,
2.6454905019E-2,
-1.1784741703E-1,
1.4142135263E0,
]);
if const { P::POLICY.check_overflow } {
y1 = x0.cmp_lt(V::ONE).select(V::NAN, y1);
}
y2 = x_small.select(y1, y2);
}
y2
}
#[inline(always)]
fn atanh<P: Policy>(self) -> Self {
let x = self.abs().flush_denormals::<P>();
let x_small = x.cmp_lt(V::HALF);
let mut y2 = V::EMPTY;
if const { P::POLICY.avoid_branching } || !x_small.all() {
let one = V::ONE;
y2 = ((one + x) / (one - x)).ln_p::<P>().scale(0.5);
if const { P::POLICY.check_overflow } {
let y3 = x.cmp_eq(one).select(V::INFINITY, V::NAN);
y2 = x.cmp_ge(one).select(y3, y2);
}
if const { P::POLICY.avoid_precision_branches() } {
return y2.mul_sign(self);
}
}
if const { P::POLICY.avoid_branching } || x_small.any() {
let x2 = x * x;
#[rustfmt::skip]
let y1 = x2.poly_rev_p::<P, _>(&[
1.81740078349E-1,
8.24370301058E-2,
1.46691431730E-1,
1.99782164500E-1,
3.33337300303E-1,
])
.mul_adde(x2 * x, x);
y2 = x_small.select(y1, y2);
}
y2.mul_sign(self)
}
#[inline(always)]
fn exp<P: Policy>(self) -> Self {
exp_f_internal::<P, Self, EXP_MODE_EXP>(self)
}
#[inline(always)]
fn exph<P: Policy>(self) -> Self {
exp_f_internal::<P, Self, EXP_MODE_EXPH>(self)
}
#[inline(always)]
fn exp2<P: Policy>(self) -> Self {
exp_f_internal::<P, Self, EXP_MODE_POW2>(self)
}
#[inline(always)]
fn exp10<P: Policy>(self) -> Self {
exp_f_internal::<P, Self, EXP_MODE_POW10>(self)
}
#[inline(always)]
fn exp_m1<P: Policy>(self) -> Self {
exp_f_internal::<P, Self, EXP_MODE_EXPM1>(self)
}
#[inline(always)]
fn exp2_m1<P: Policy>(self) -> Self {
exp_f_internal::<P, Self, EXP_MODE_POW2M1>(self)
}
#[inline(always)]
fn exp10_m1<P: Policy>(self) -> Self {
exp_f_internal::<P, Self, EXP_MODE_POW10M1>(self)
}
#[inline(always)]
fn powf<P: Policy>(self, y: Self) -> Self {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && Self::NATIVE_CAP.has(NativeCapability::POWF) } {
return unsafe { self.native_powf::<P>(y) };
}
let x0 = self;
let y = y.flush_denormals::<P>();
if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
return (x0.log2_p::<MediumPrecision<P>>() * y).exp2_p::<P>();
}
let ln2f_hi: V = crate::const_splat!(f32: 0.693359375); let ln2f_lo: V = crate::const_splat!(f32: -2.12194440e-4);
let log2e = V::LOG2_E;
let ln2 = V::LN_2;
let zero = V::ZERO;
let one = V::ONE;
let half = V::HALF;
let x1 = x0.abs().flush_denormals::<P>();
let mut x = fraction2::<V>(x1);
let blend = x.cmp_gt(crate::const_splat!(f32: SQRT_2 * 0.5));
x.add_assign_c(!blend, x); x -= one;
let x2 = x * x;
let mut lg1 = x.poly_rev_p::<P, _>(&[
7.0376836292E-2,
-1.1514610310E-1,
1.1676998740E-1,
-1.2420140846E-1,
1.4249322787E-1,
-1.6668057665E-1,
2.0000714765E-1,
-2.4999993993E-1,
3.3333331174E-1,
]);
lg1 *= x2 * x;
let ef = V::cast_from(exponent::<V>(x1)).add_c(blend, one);
let e1 = (ef * y).round();
let yr = ef.mul_sube(y, e1);
let lg = half.nmul_adde(x2, x) + lg1;
let x2err = (half * x).mul_sube(x, half * x2);
let lgerr = half.mul_adde(x2, lg - x) - lg1;
let e2 = (lg * y * log2e).round();
let mut v = e2.nmul_adde(ln2f_lo, lg.mul_sube(y, e2 * ln2f_hi));
v -= (lgerr + x2err).mul_sube(y, yr * ln2);
let mut x = v;
let e3 = (x * log2e).round();
x = e3.nmul_adde(ln2, x);
let x2 = x * x;
let z = x
.poly_rev_p::<P, _>(&[1.0 / 5040.0, 1.0 / 720.0, 1.0 / 120.0, 1.0 / 24.0, 1.0 / 6.0, 1.0 / 2.0])
.mul_adde(x2, x + one);
let ee = e1 + e2 + e3;
let ei: V::SignedBits = ee.fast_cast();
let ej = ei + (V::SignedBits::from_bits(z.abs()) >> 23);
let mut z = V::from_bits(V::SignedBits::from_bits(z) + (ei << 23));
if const { !P::POLICY.check_overflow } {
return y.cmp_eq(zero).select(one, z);
}
let overflow =
ej.cmp_ge(V::SignedBits::splat(0x0FF)).cast::<V::Mask>() | ee.cmp_gt(crate::const_splat!(f32: 300.0));
let underflow =
ej.cmp_le(V::SignedBits::splat(0x000)).cast::<V::Mask>() | ee.cmp_lt(crate::const_splat!(f32: -300.0));
let xfinite = x0.is_finite();
let yfinite = y.is_finite();
let efinite = ee.is_finite();
let xzero = x0.is_zero_or_subnormal();
let xsign = x0.is_negative();
z = underflow.select(zero, z);
z = overflow.select(V::INFINITY, z);
let yzero = y.cmp_eq(zero);
let yneg = y.cmp_lt(zero);
z = xzero.select(yneg.select(V::INFINITY, yzero.select(one, zero)), z);
let mut yodd = zero;
if xsign.any() {
let yint = y.cmp_eq(y.round());
yodd = V::from_bits(y.into_bits::<V::Bits>() << 31);
let z0 = x0.cmp_eq(zero).select(z, V::NAN);
let z1 = yint.select(z | yodd, z0);
yodd = yint.select(yodd, zero);
z = xsign.select(z1, z);
}
let not_special = xfinite & yfinite & (efinite | xzero);
if crate::likely(not_special.all()) {
return z; }
let z1 = (yfinite & efinite).select(
z,
x1.cmp_eq(one)
.select(one, (x1.cmp_gt(one) ^ y.is_negative()).select(V::INFINITY, zero)),
);
let z1 = xfinite.select(
z1,
yzero.select(
one,
yneg.select(
yodd & z, V::ternlog::<{ crate::ternlog_imm!(A | (B & C)) }>(x1, x0, yodd),
),
),
);
(x0.is_nan() | y.is_nan()).select(x0 + y, z1)
}
#[inline(always)]
fn cbrt<P: Policy>(self) -> Self {
let x = self.flush_denormals::<P>();
let b1: V::Bits = crate::const_splat!(u32: 709958130); let b2: V::Bits = crate::const_splat!(u32: 642849266); let m: V::Bits = crate::const_splat!(u32: 0x7fffffff);
let x1p24 = x * crate::const_splat!(f32: f32::from_bits(0x4b800000));
let hx0: V::Bits = x.into_bits::<V::Bits>() & m;
let x_small = hx0.cmp_lt(crate::const_splat!(u32: 0x00800000));
let xs = x_small.select(x1p24, x);
let b = x_small.select(b2, b1);
let mut ui: V::Bits = xs.into_bits();
let mut hx = ui & m;
hx = hx / Divider::u32(3) + b;
ui &= V::Bits::splat(0x80000000);
ui |= hx;
let mut t = V::from_bits(ui);
if const { P::POLICY.precision.ge(PrecisionPolicy::Best) || !Self::HAS_TRUE_FMA } {
let mut td: Self::ExtendedPrecision = t.cast();
let xd: Self::ExtendedPrecision = x.cast();
for _ in 0..2 {
let r = td * td * td;
let rxd = xd + r;
td *= (xd + rxd) / (r + rxd);
}
t = td.cast();
} else {
let two = V::TWO;
for _ in 0..2 {
let t3 = t * t * t;
t *= two.mul_add(x, t3) / two.mul_add(t3, x); }
if const { P::POLICY.precision.ge(PrecisionPolicy::Average) } {
let t2 = t * t;
t -= t2.mul_sub(t, x) / (t2 * crate::const_splat!(f32: 3.0)); }
}
if const { !P::POLICY.check_overflow } {
return x.cmp_eq(V::ZERO).select(x, t);
}
(hx0.cmp_gt(V::Bits::splat(0x7f800000)) | hx0.cmp_eq(V::Bits::ZERO)).select(x, t)
}
#[inline(always)]
fn ln<P: Policy>(self) -> Self {
ln_f_internal::<P, Self, false>(self)
}
#[inline(always)]
fn ln_1p<P: Policy>(self) -> Self {
ln_f_internal::<P, Self, true>(self)
}
#[inline(always)]
fn log2<P: Policy>(self) -> Self {
ln_2_internal::<P, Self>(self)
}
#[inline(always)]
fn log10<P: Policy>(self) -> Self {
ln_10_internal::<P, Self>(self)
}
#[inline(always)]
fn ln1m_expnx<P: Policy>(self) -> Self {
let x = self;
if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
return x.ln1m_expnx_ext_p::<P>(x.ln_p::<P>());
}
(V::ONE - (-x).exp_p::<P>()).ln_p::<P>()
}
#[inline(always)]
fn ln1m_expnx_ext<P: Policy>(self, lnx: Self) -> Self {
let x = self;
if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
let x = x.flush_denormals::<P>();
const X1: f32 = 9.1;
const X2: f32 = 16.3;
const B: f32 = 1.0 / (X2 - X1); const AB: f32 = X1 / (X2 - X1);
let u1 = x.mul_sube(crate::const_splat!(f32: B), crate::const_splat!(f32: AB));
let mut u1 = u1.min(V::ONE).max(V::ZERO);
if const { P::POLICY.precision.eq(PrecisionPolicy::Medium) } {
u1 = u1.smoothstep_p::<P, 2>(None);
}
let c = x.poly_rational_p::<P, _, _>(
&[0.0, 0.5, 0.0439145, 0.0116566, 0.000713523, 0.0000392684],
&[
1.0,
0.171161,
0.0375791,
0.0038616,
0.000283035,
7.93625e-6,
-1.02103e-8,
7.10327e-12,
],
);
let mut res = u1.lerp_p::<P>(lnx - c, V::ZERO);
if const { P::POLICY.check_overflow } {
res = res.cmp_lt(V::ZERO).select(V::NAN, res);
res = res.cmp_eq(V::ZERO).select(V::NEG_INFINITY, res);
}
return res;
}
(V::ONE - (-x).exp_p::<P>()).ln_p::<P>()
}
}
impl<V: FloatVectorWithBits<Element = f32>> SpecializedRealMath<f32> for V {
#[inline(always)]
fn wrap_angle<P: Policy>(self) -> Self {
let x = self;
let n = ((x + Self::PI) * (Self::FRAC_1_PI * Self::HALF)).floor();
if const { Self::HAS_TRUE_FMA || P::POLICY.precision.le(PrecisionPolicy::Average) } {
return n.nmul_adde(Self::TAU, x);
}
let tau_hi: V = crate::const_splat!(f32: hexf::hexf32!("0x1.921fb60000000p+2"));
let tau_lo: V = crate::const_splat!(f32: hexf::hexf32!("-0x1.777a5c0000000p-23"));
(x - n * tau_hi) - n * tau_lo
}
#[inline(always)]
fn atan2<P: Policy>(self, x: Self) -> Self {
let y = self;
let neg_one = V::NEG_ONE;
let zero = V::ZERO;
let x1 = x.abs().flush_denormals::<P>();
let y1 = y.abs().flush_denormals::<P>();
let swap_xy = y1.cmp_gt(x1);
if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
let (a, b) = (x1, y1);
let n = swap_xy.select(b, a);
let d = swap_xy.select(a, b);
let mut k = n / d;
if const { P::POLICY.check_overflow } {
let b_eq_zero = b.cmp_eq(V::ZERO);
let ab_eq = a.cmp_eq(b);
k = ab_eq.select(V::ONE, k);
k = b_eq_zero.select(V::ZERO, k);
}
let s = k.flush_denormals::<P>();
let t = s * s;
let mut r = t.mul_adde(s * crate::const_splat!(f32: 0.43157974), s)
/ t.mul_adde(
crate::const_splat!(f32: 0.05831938),
crate::const_splat!(f32: 0.76443945),
)
.mul_adde(t, V::ONE);
r = swap_xy.select(V::FRAC_PI_2 - r, r);
r = x.select_negative(V::PI - r, r);
return r.copysign(y);
}
let mut x2 = swap_xy.select(y1, x1);
let mut y2 = swap_xy.select(x1, y1);
if const { P::POLICY.check_overflow } {
let both_infinite = x.is_infinite() & y.is_infinite();
x2 = both_infinite.select(x2 & neg_one, x2); y2 = both_infinite.select(y2 & neg_one, y2); }
let t = y2 / x2;
let not_small = t.cmp_ge(crate::const_splat!(f32: SQRT_2 - 1.0));
let a = t + neg_one.zz(not_small);
let b = V::ONE + t.zz(not_small);
let s = V::FRAC_PI_4.zz(not_small);
let z = a / b;
let z2 = z * z;
let mut re = z2
.poly_rev_p::<P, _>(&[8.05374449538E-2, -1.38776856032E-1, 1.99777106478E-1, -3.33329491539E-1])
.mul_adde(z2 * z, z + s);
re = swap_xy.select(V::FRAC_PI_2 - re, re);
re = (x | y).is_zero().select(zero, re); re = x.select_negative(V::PI - re, re);
re.copysign(y)
}
}
#[thermite_macros::dispatch(V, thermite = "crate")]
fn payne_hanek_reduction<P: Policy, V: FloatVectorWithBits<Element = f32>>(xa: &V) -> (V, V, V::Bits) {
let xa_bits: V::Bits = xa.into_bits();
let exp = (V::SignedBits::from_bits(xa_bits.shri::<23>()) & V::SignedBits::splat(0xFF)) - V::SignedBits::splat(127);
let exp_u: V::Unsigned = V::Bits::from_bits(exp.max(V::SignedBits::ZERO)).cast();
let sig = (xa_bits & V::Bits::splat(0x007FFFFF)) | V::Bits::splat(0x00800000);
const INVPI_TABLE: [u32; 7] = [
0x00000000, 0xA2F9836E, 0x4E441529, 0xFC2757D1, 0xF534DDC0, 0xDB629599, 0x3C439041,
];
let biased = exp_u + V::Unsigned::splat(6); let idx: V::Unsigned = biased.shri::<5>();
let shift = biased & V::Unsigned::splat(31);
let inv_shift = (V::Unsigned::splat(32) - shift) & V::Unsigned::splat(31);
let c0 = unsafe { V::Unsigned::lookup_unchecked(&INVPI_TABLE, idx) };
let c1 = unsafe { V::Unsigned::lookup_unchecked(&INVPI_TABLE, idx + V::Unsigned::ONE) };
let c2 = unsafe { V::Unsigned::lookup_unchecked(&INVPI_TABLE, idx + V::Unsigned::TWO) };
let mask = shift.cmp_ne(V::Unsigned::ZERO);
let aligned_hi = c0.shlv(shift) | c1.shrv(inv_shift).zz(mask);
let aligned_lo = c1.shlv(shift) | c2.shrv(inv_shift).zz(mask);
let aligned_hi: V::Bits = aligned_hi.cast();
let aligned_lo: V::Bits = aligned_lo.cast();
let prod_hi = sig.mullo(aligned_hi); let prod_lo = sig.mulhi(aligned_lo); let mid_bits = prod_hi + prod_lo; let prod_lo_lo = sig.mullo(aligned_lo);
let mut q_ph: V::Bits = (mid_bits.shri::<29>()) & V::Bits::splat(3);
let fraction_hi_int = mid_bits & V::Bits::splat(0x1FFFFFFF);
let frac_hi_bits = fraction_hi_int.shri::<6>() | V::Bits::splat(0x3F800000);
let frac_hi = V::from_bits(frac_hi_bits) - V::ONE;
let residual = (fraction_hi_int & V::Bits::splat(0x3F)).shli::<18>() | prod_lo_lo.shri::<14>();
let frac_lo_int: V::SignedBits = residual.cast();
let frac_lo = V::cast_from(frac_lo_int) * crate::const_splat!(f32: f32::from_bits(0x28000000));
let needs_round = frac_hi.cmp_ge(V::HALF);
let frac_hi = frac_hi.sub_c(needs_round, V::ONE);
q_ph = q_ph.add_c(needs_round.cast(), V::Bits::ONE);
let pi2_hi = V::FRAC_PI_2;
let pi2_lo = crate::const_splat!(f32: -4.37113882867379288655e-08);
let x_hi = frac_hi * pi2_hi;
let x_lo = frac_hi.mul_add(pi2_hi, -x_hi) + frac_hi * pi2_lo + frac_lo * pi2_hi;
(x_hi, x_lo, q_ph)
}
#[inline(always)]
pub(crate) fn trig_range_reduction<P: Policy, V: FloatVectorWithBits<Element = f32>, const PI: bool>(
mut xa: V,
) -> (V, V, V::Bits) {
let mut is_large = V::Mask::FALSY;
let y0 = if PI {
xa + xa } else {
is_large = xa.cmp_gt(crate::const_splat!(<V> = <V: FloatVector> f32: {
match V::HAS_TRUE_FMA {
true => 1e7,
false => 1e5,
}
}));
if const { P::POLICY.check_overflow && P::POLICY.precision.le(PrecisionPolicy::Average) } {
xa = xa.nz(is_large); }
xa.scale(FloatConsts::FRAC_2_PI)
};
let y = y0.round();
let mut q: V::Bits = V::SignedBits::fast_cast_from(y).into_bits();
let mut x = if PI {
y.nmul_adde(V::HALF, xa).scale(FloatConsts::PI)
} else if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
y.nmul_adde(V::FRAC_PI_2, xa)
} else {
let dp1f = crate::const_splat!(f32: 0.78515625 * 2.0);
let dp2f = crate::const_splat!(f32: 2.4187564849853515625E-4 * 2.0);
let dp3f = crate::const_splat!(f32: 3.77476681023836135864E-8 * 2.0);
let dp4f = crate::const_splat!(f32: 1.28164145962728071027E-12 * 2.0);
if const { V::HAS_TRUE_FMA } {
y.nmul_add(dp4f, y.nmul_add(dp3f, y.nmul_add(dp2f + dp1f, xa)))
} else {
(((xa - y * dp1f) - y * dp2f) - y * dp3f) - y * dp4f
}
};
let mut x_lo = V::ZERO;
if const { P::POLICY.precision.gt(PrecisionPolicy::Average) && !PI }
&& (P::POLICY.avoid_branching || is_large.any())
{
let (x_ph, x_lo_ph, q_ph) = payne_hanek_reduction::<P, V>(&xa);
x = is_large.select(x_ph, x);
x_lo = x_lo_ph.zz(is_large); q = is_large.select(q_ph, q);
}
(x, x_lo, q)
}
#[inline(always)]
fn sin_cos_f_internal<P: Policy, V: FloatVectorWithBits<Element = f32>, const PI: bool, const SINGLE: bool>(
xx: V,
) -> (V, V) {
if const { SINGLE && P::POLICY.precision.le(PrecisionPolicy::Worst) } {
#[inline(always)] #[rustfmt::skip]
fn inner<V: FloatVector<Element = f32>>(mut x: V) -> V {
x *= x.abs().mul_sube(
crate::const_splat!(f32: 16.0),
crate::const_splat!(f32: 8.0),
);
let p = crate::const_splat!(f32: 0.22400815333595678);
x.mul_adde(x.abs().mul_sube(p, p), x)
}
let xx = xx.flush_denormals::<P>();
let m = if PI {
V::HALF } else {
crate::const_splat!(f32: FRAC_1_PI / 2.0)
};
return if const { V::HAS_TRUE_FMA && V::ISA.has_instruction_level_parallelism() } {
(
inner::<V>(xx.mul_sub(m, V::HALF) - (xx * m).floor()), inner::<V>(xx.mul_sub(m, V::FRAC_1_4) - xx.mul_add(m, V::FRAC_1_4).floor()), )
} else {
let x = m * xx;
(
inner::<V>((x - V::HALF) - x.floor()), inner::<V>((x - V::FRAC_1_4) - (x + V::FRAC_1_4).floor()), )
};
}
let xa = xx.abs().flush_denormals::<P>();
let (x, x_lo, q) = trig_range_reduction::<P, V, PI>(xa);
let x2 = x * x;
let mut x0 = x;
if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
x0 += x_lo;
}
#[rustfmt::skip]
let s = x2.poly_rev_p::<P, _>(&[
-1.9515295891E-4,
8.3321608736E-3,
-1.6666654611E-1,
])
.mul_adde(x2 * x, x0);
#[rustfmt::skip]
let mut c = x2.poly_rev_p::<P, _>(&[
2.443315711809948E-5,
-1.388731625493765E-3,
4.166664568298827E-2,
])
.mul_adde(x2 * x2, x2.nmul_adde(V::HALF, V::ONE));
if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
c = x.nmul_adde(x_lo, c);
}
let swap = (q & V::Bits::ONE).cmp_ne(V::Bits::ZERO);
let sin1 = swap.select(c, s);
let cos1 = swap.select(s, c);
let signsin = V::from_bits(q.shli::<30>()) ^ xx;
let signcos = V::from_bits((q + V::Bits::ONE).shri::<1>().shli::<31>());
(sin1.mul_sign(signsin), cos1 ^ signcos)
}
#[inline(always)]
fn asin_f_internal<P: Policy, V: FloatVectorWithBits<Element = f32>, const ACOS: bool>(x: V) -> V {
let xa = x.abs().flush_denormals::<P>();
if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
let m = xa.min(V::ONE);
let a0 = (V::ONE - m).sqrt();
let a1 = m.poly_rev_p::<P, _>(&[-0.02164095, 0.077980478, -0.213300989, FRAC_PI_2]);
if ACOS {
if const { V::HAS_TRUE_FMA && V::ISA.has_instruction_level_parallelism() } {
return x.select_negative(a0.nmul_add(a1, V::PI), a0 * a1);
}
let a = a0 * a1;
return x.select_negative(V::PI - a, a);
} else {
return a0.nmul_adde(a1, V::FRAC_PI_2).copysign(x);
}
}
let is_big = xa.cmp_gt(V::HALF);
let x1 = V::HALF * (V::ONE - xa);
let x3 = is_big.select(x1, xa * xa);
let x4 = is_big.select(x1.sqrt(), xa);
#[rustfmt::skip]
let z = x3.poly_rev_p::<P, _>(&[
4.2163199048E-2,
2.4181311049E-2,
4.5470025998E-2,
7.4953002686E-2,
1.6666752422E-1,
])
.mul_adde(x3 * x4, x4);
let z1 = z + z;
if ACOS {
let z1 = x.select_negative(V::PI - z1, z1);
let z2 = V::FRAC_PI_2 - z.mul_sign(x);
is_big.select(z1, z2)
} else {
let z1 = V::FRAC_PI_2 - z1;
is_big.select(z1, z).mul_sign(x)
}
}
#[inline(always)]
fn pow2n_f<V: FloatVectorWithBits<Element = f32>>(n: V) -> V {
let pow2_23: V = crate::const_splat!(f32: 8388608.0);
let bias: V = crate::const_splat!(f32: 127.0);
V::from_bits(V::Bits::from_bits(n + (bias + pow2_23)).shli::<23>())
}
#[inline(always)]
fn pow2n_f_safe<V: FloatVectorWithBits<Element = f32>>(n: V) -> (V, V) {
let half = n.scale(0.5).floor();
let other = n - half;
(pow2n_f(half), pow2n_f(other))
}
#[inline(always)]
fn exp_f_internal<P: Policy, V: FloatVectorWithBits<Element = f32>, const MODE: u8>(x0: V) -> V {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) } {
if const { V::NATIVE_CAP.has(NativeCapability::EXP) && MODE == EXP_MODE_EXP } {
return unsafe { x0.native_exp::<P>() };
}
if const { V::NATIVE_CAP.has(NativeCapability::EXP2) && MODE == EXP_MODE_POW2 } {
return unsafe { x0.native_exp2::<P>() };
}
}
let x0 = x0.flush_denormals::<P>();
let mut x = x0;
let mut r;
let mut z = if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
let mut t = match MODE {
EXP_MODE_EXP | EXP_MODE_EXPH | EXP_MODE_EXPM1 => x.scale(FloatConsts::LOG2_E),
EXP_MODE_POW10 | EXP_MODE_POW10M1 => x.scale(FloatConsts::LOG2_10),
EXP_MODE_POW2 | EXP_MODE_POW2M1 => x,
_ => unreachable!("Invalid MODE for exp_f_internal"),
};
if const { !P::POLICY.check_overflow } {
t = t.clamp(crate::const_splat!(f32: -127.0), crate::const_splat!(f32: 127.0));
}
let fi = t.floor();
let f = t - fi;
let i: V::SignedBits = fi.fast_cast();
let cf = if const { P::POLICY.precision.gt(PrecisionPolicy::Worst) } {
f.poly_rev_p::<P, _>(&[
0.000021428975742310286, 0.000143863057019189000, 0.001341646537184715271, 0.009614554233849048615, 0.055504892021417617798, 0.240226432681083679199, 0.693147182464599609375, 1.0, ])
} else {
f.poly_rev_p::<P, _>(&[0.0781455737, 0.226173572, 0.695556856, 1.0])
};
let ci = V::SignedBits::from_bits(cf) + (i << 23);
let z = V::from_bits(ci);
match MODE {
EXP_MODE_EXPH => z.scale(0.5),
EXP_MODE_EXPM1 | EXP_MODE_POW2M1 | EXP_MODE_POW10M1 => z - V::ONE,
EXP_MODE_EXP | EXP_MODE_POW2 | EXP_MODE_POW10 => z,
_ => unreachable!("Invalid MODE for exp_f_internal"),
}
} else {
match MODE {
EXP_MODE_POW2 | EXP_MODE_POW2M1 => {
r = x0.round();
x -= r;
x *= V::LN_2;
}
EXP_MODE_POW10 | EXP_MODE_POW10M1 => {
let log10_2_hi: V = crate::const_splat!(f32: -0.301025391); let log10_2_lo: V = crate::const_splat!(f32: -4.60503907E-6);
r = (x0 * crate::const_splat!(f32: LN_10 * LOG2_E)).round();
x = r.mul_adde(log10_2_hi, x); x = r.mul_adde(log10_2_lo, x); x *= V::LN_10;
}
EXP_MODE_EXP | EXP_MODE_EXPM1 | EXP_MODE_EXPH => {
let ln2f_hi: V = crate::const_splat!(f32: -0.693359375);
let ln2f_lo: V = crate::const_splat!(f32: 2.12194440e-4);
r = x0.scale(FloatConsts::LOG2_E).round();
x = r.mul_adde(ln2f_hi, x); x = r.mul_adde(ln2f_lo, x);
if const { MODE == EXP_MODE_EXPH } {
r -= V::ONE;
}
}
_ => unreachable!("Invalid MODE for exp_f_internal"),
}
let z = x
.poly_rev_p::<P, _>(&[1.0 / 5040.0, 1.0 / 720.0, 1.0 / 120.0, 1.0 / 24.0, 1.0 / 6.0, 1.0 / 2.0])
.mul_adde(x * x, x);
if const { !P::POLICY.check_overflow } {
r = r.clamp(crate::const_splat!(f32: -127.0), crate::const_splat!(f32: 127.0));
}
if const { P::POLICY.precision.le(PrecisionPolicy::Average) } {
let n2 = pow2n_f::<V>(r);
match MODE {
EXP_MODE_EXPM1 | EXP_MODE_POW2M1 | EXP_MODE_POW10M1 => z.mul_adde(n2, n2 - V::ONE),
_ => z.mul_adde(n2, n2), }
} else {
let (n2a, n2b) = pow2n_f_safe::<V>(r);
match MODE {
EXP_MODE_EXPM1 | EXP_MODE_POW2M1 | EXP_MODE_POW10M1 => {
z.mul_adde(n2a, n2a - V::ONE).mul_adde(n2b, n2b - V::ONE)
}
_ => z.mul_adde(n2a, n2a) * n2b, }
}
};
if const { P::POLICY.check_overflow } {
let mut in_range = x0.is_finite();
if const { P::POLICY.precision.gt(PrecisionPolicy::Average) } {
#[rustfmt::skip]
let (min_x, max_x) = const { match MODE {
EXP_MODE_EXP => (-103.97, 88.72), EXP_MODE_EXPM1 => (-87.0, 88.72), EXP_MODE_EXPH => (-103.97, 89.42), EXP_MODE_POW2 => (-150.0, 128.0), EXP_MODE_POW2M1 => (-150.0, 128.0), EXP_MODE_POW10 => (-45.15, 38.53), EXP_MODE_POW10M1 => (-45.15, 38.53),
_ => panic!("Invalid MODE for exp_f_internal"), }};
in_range &= x0.cmp_ge(V::splat(min_x)) & x0.cmp_le(V::splat(max_x));
} else {
#[rustfmt::skip]
let max_x = const { match MODE {
EXP_MODE_EXP => 87.3,
EXP_MODE_POW2 | EXP_MODE_POW2M1 => 126.0,
EXP_MODE_POW10 | EXP_MODE_POW10M1 => 37.9,
EXP_MODE_EXPH | EXP_MODE_EXPM1 => 89.0,
_ => panic!("Invalid MODE for exp_f_internal"),
}};
in_range &= x0.abs().cmp_le(V::splat(max_x)); }
#[rustfmt::skip]
let underflow_value = const { match MODE {
EXP_MODE_EXPM1 | EXP_MODE_POW2M1 | EXP_MODE_POW10M1 => V::NEG_ONE,
_ => V::ZERO,
} };
r = x0.select_negative(underflow_value, V::INFINITY);
z = in_range.select(z, r);
z = x0.is_nan().select(x0, z);
}
z
}
#[inline(always)]
fn fraction2<V: FloatVectorWithBits<Element = f32>>(x: V) -> V {
let b = crate::const_splat!(f32: f32::from_bits(0x007FFFFF));
let c = crate::const_splat!(f32: f32::from_bits(0x3F000000));
V::ternlog::<{ crate::ternlog_imm!((A & B) | C) }>(x, b, c)
}
#[inline(always)]
fn exponent<V: FloatVectorWithBits<Element = f32>>(x: V) -> V::SignedBits {
V::SignedBits::from_bits((V::Bits::from_bits(x).shli::<1>()).shri::<24>()) - V::SignedBits::splat(0x7F)
}
#[inline(always)]
fn ln_2_internal<P: Policy, V: FloatVectorWithBits<Element = f32>>(x: V) -> V {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && V::NATIVE_CAP.has(NativeCapability::LOG2) } {
return unsafe { x.native_log2::<P>() };
}
if const { P::POLICY.precision.eq(PrecisionPolicy::Worst) } {
return V::cast_from(V::SignedBits::from_bits(x)).mul_sube(
crate::const_splat!(f32: 1.1920928955078125e-7),
crate::const_splat!(f32: 126.94269504),
);
}
ln_f_internal::<P, V, false>(x).scale(FloatConsts::LOG2_E)
}
#[inline(always)]
fn ln_10_internal<P: Policy, V: FloatVectorWithBits<Element = f32>>(x: V) -> V {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && V::NATIVE_CAP.has(NativeCapability::LOG2) } {
return unsafe { x.native_log2::<P>().scale(FloatConsts::LOG10_2) };
}
if const { P::POLICY.precision.eq(PrecisionPolicy::Worst) } {
return V::cast_from(V::SignedBits::from_bits(x)).mul_sube(
crate::const_splat!(f32: 3.5885571887588505e-8),
crate::const_splat!(f32: 38.213558906),
);
}
ln_f_internal::<P, V, false>(x).scale(FloatConsts::LOG10_E)
}
#[inline(always)]
fn ln_f_internal<P: Policy, V: FloatVectorWithBits<Element = f32>, const P1: bool>(x0: V) -> V {
if const { P::POLICY.precision.le(PrecisionPolicy::Average) && V::NATIVE_CAP.has(NativeCapability::LN) && !P1 } {
return unsafe { x0.native_ln::<P>() };
}
if const { P::POLICY.precision.eq(PrecisionPolicy::Worst) } {
let x1 = if P1 { x0 + V::ONE } else { x0 };
return V::cast_from(V::SignedBits::from_bits(x1)).mul_sube(
crate::const_splat!(f32: 8.2629582881927490e-8),
crate::const_splat!(f32: 87.989971088),
);
}
if const { P::POLICY.precision.eq(PrecisionPolicy::Medium) } {
let a = V::SignedBits::from_bits(x0);
let e = (a - V::SignedBits::splat(0x3f2aaaab)) & V::SignedBits::splat(0xff800000u32 as i32);
let i = V::cast_from(e) * crate::const_splat!(f32: 1.19209290e-7);
let mut f = V::from_bits(a - e);
if !P1 {
f -= V::ONE;
}
let s = f * f;
let r = f.mul_adde(
crate::const_splat!(f32: 0.230836749),
crate::const_splat!(f32: -0.279208571),
); let t = f.mul_adde(
crate::const_splat!(f32: 0.331826031),
crate::const_splat!(f32: -0.498910338),
); let r = r.mul_adde(s, t).mul_adde(s, f);
let r = i.mul_adde(crate::const_splat!(f32: 0.693147182), r);
return r;
}
let x0 = x0.flush_denormals::<P>();
let ln2f_hi = crate::const_splat!(f32: 0.693359375);
let ln2f_lo = crate::const_splat!(f32: -2.12194440E-4);
let x1 = if P1 { x0 + V::ONE } else { x0 };
let mut x = fraction2::<V>(x1);
let mut e = exponent::<V>(x1);
let blend = x.cmp_gt(crate::const_splat!(f32: SQRT_2 * 0.5));
x = x.add_c(!blend, x);
e = e.add_c(blend.cast(), V::SignedBits::ONE);
let fe: V = e.cast();
let xp1 = x - V::ONE;
x = if P1 {
e.cmp_eq(V::SignedBits::ZERO).select(x0, xp1)
} else {
xp1 };
let x2 = x * x;
let mut res = x.poly_rev_p::<P, _>(&[
7.0376836292E-2,
-1.1514610310E-1,
1.1676998740E-1,
-1.2420140846E-1,
1.4249322787E-1,
-1.6668057665E-1,
2.0000714765E-1,
-2.4999993993E-1,
3.3333331174E-1,
0.0, ]);
res = fe.mul_adde(ln2f_lo, res.mul_adde(x2, x2.nmul_adde(V::HALF, x)));
res = fe.mul_adde(ln2f_hi, res);
if const { !P::POLICY.check_overflow } {
return res;
}
let overflow = !x1.is_finite();
let underflow = x1.cmp_lt(crate::const_splat!(f32: 1.17549435e-38));
if const { !P::POLICY.avoid_branching } && crate::likely((overflow | underflow).none()) {
return res;
}
res = underflow.select(V::NAN, res); res = x1.is_zero_or_subnormal().select(V::NEG_INFINITY, res); res = overflow.select(x1, res); res = (x1.is_infinite() & x1.is_negative()).select(V::NAN, res);
res
}