1use crate::{dd::Double, exp::exp_narrow, float::Native, ln::ln_narrow, recipe::{Ops, root2}, table};
3#[inline]
5fn positive_finite(a: f64) -> bool { a.to_bits().wrapping_sub(1) < f64::INFINITY.to_bits() - 1 }
6
7#[inline]
8pub(crate) fn parity<R: Native>(y: R) -> (bool, bool) {
9 let half = R::narrow(0.5) * y;
10 let whole = y.trunc() == y;
11 (whole, whole & (half.trunc() != half))
12}
13
14pub(crate) fn formula<F: crate::Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V) -> O::V {
16 if let Some(k) = o.constant(y) { if let Some(result) = crate::recipe::power(o, Exponent::of(k.wide(), o.fused()), x) { return result; } }
17 o.binary(crate::recipe::Binary::Power, x, y)
18}
19
20pub(crate) fn root_formula<F: crate::Float, O: Ops<F> + ?Sized>(o: &O, n: O::V, y: O::V) -> Option<O::V> {
22 let k = o.constant(n)?;
23 rooted(o, Degree::of(k.wide()), k < F::zero(), y)
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28enum Degree { One, Two, Three, Four, Eight, Half, General }
29
30impl Degree {
31 #[inline(always)]
33 fn of(n: f64) -> Self {
34 match n.abs() { 1.0 => Self::One, 2.0 => Self::Two, 3.0 => Self::Three, 4.0 => Self::Four, 8.0 => Self::Eight, 0.5 => Self::Half, _ => Self::General }
35 }
36}
37
38#[inline(always)]
40fn rooted<F: crate::Float, O: Ops<F> + ?Sized>(o: &O, d: Degree, negative: bool, y: O::V) -> Option<O::V> {
41 let r = match d {
42 Degree::One => y,
43 Degree::Two => root2(o, y),
44 Degree::Three => cbrt_formula(o, y),
45 Degree::Four => root2(o, root2(o, y)),
46 Degree::Eight => root2(o, root2(o, root2(o, y))),
47 Degree::Half => {
48 let x = if negative { o.reciprocal(y) } else { y };
49 return Some(o.select(o.less(y, o.number(F::zero())), o.number(F::nan()), o.multiply(x, x)));
50 }
51 Degree::General => return None,
52 };
53 Some(if negative { o.reciprocal(r) } else { r })
54}
55
56#[inline(always)]
58fn power_of<R: Native, const FUSED: bool>(x: f64, y: f64, divide: bool) -> f64 {
59 let a = x.abs();
60 if R::WIDE {
61 let l = table::ln::<FUSED>(a);
62 let d = if divide { l.div::<FUSED>(Double::new(y, 0.0)) } else { l.mul_float::<FUSED>(y) };
63 table::exp::<FUSED>(d.hi, d.lo)
64 } else {
65 let l = if positive_finite(a) { ln_narrow::<FUSED>(a) } else { (a - 1.0) * f64::INFINITY };
66 exp_narrow::<FUSED>(if divide { l / y } else { l * y })
67 }
68}
69
70#[inline]
72fn signed<R: Native>(r: R, x: R, odd: bool, exists: bool) -> R {
73 let r = if x.is_sign_negative() & odd { -r } else { r };
74 if (x < R::zero()) & (x > R::neg_infinity()) & !exists { R::nan() } else { r }
75}
76#[inline(always)]
78fn fast<R: Native, const FUSED: bool>(x: R, y: R, divide: bool) -> Option<R> {
79 let (a, y) = (x.wide(), y.wide());
80 let (r, normal) = if R::WIDE {
81 let l = table::ln_normal::<FUSED>(a);
82 let d = if divide { l.div::<FUSED>(Double::new(y, 0.0)) } else { l.mul_float::<FUSED>(y) };
83 (table::exp_normal::<FUSED>(d.hi, d.lo), d.hi.abs() < table::NORMAL)
84 } else { let l = ln_narrow::<FUSED>(a); (exp_narrow::<FUSED>(if divide { l / y } else { l * y }), true) };
85 let base = a.to_bits().wrapping_sub(f64::MIN_POSITIVE.to_bits()) < f64::INFINITY.to_bits() - f64::MIN_POSITIVE.to_bits();
87 (base & normal & usable(y, divide)).then_some(R::narrow(r))
88}
89
90#[inline(always)]
92fn fast_single<R: Native, const FUSED: bool>(x: R, y: R, divide: bool) -> Option<R> {
93 let (ix, y) = ((x.wide() as f32).to_bits(), y.wide());
94 let l = table::log2_single::<FUSED>(ix);
95 let v = if divide { l / y } else { l * y };
96 let normal = ix.wrapping_sub(0x0080_0000) < 0x7f80_0000 - 0x0080_0000;
97 let small = (v.to_bits() >> 47 & 0xffff) < 126f64.to_bits() >> 47;
99 (normal && usable(y, divide) && small).then(|| R::narrow(table::exp2_single::<FUSED>(v)))
100}
101
102#[inline(always)]
104fn usable(y: f64, divide: bool) -> bool { y.is_finite() & !(divide & (y == 0.0)) }
105
106#[inline(always)]
108fn pow_any<R: Native, const FUSED: bool>(x: R, y: R) -> R {
109 let (whole, odd) = parity(y);
110 let r = R::narrow(power_of::<R, FUSED>(x.wide(), y.wide(), false));
111 signed(if (y == R::zero()) | (x.abs() == R::one()) { R::one() } else { r }, x, odd, whole)
112}
113
114#[inline(always)]
116fn root_any<R: Native, const FUSED: bool>(n: R, y: R) -> R {
117 let (_, odd) = parity(n);
118 let r = signed(R::narrow(power_of::<R, FUSED>(y.wide(), n.wide(), true)), y, odd, odd);
119 if (n == R::zero()) | !n.is_finite() { R::nan() } else { r }
120}
121
122#[inline]
124pub(crate) fn pow<R: Native, const FUSED: bool>(x: R, y: R) -> R {
125 let o = crate::recipe::NativeOps::<FUSED>::DEFAULT;
126 if let Some(r) = crate::recipe::power(&o, Exponent::of(y.wide(), FUSED), x) { return r; }
127 let r = if R::WIDE { fast::<R, FUSED>(x, y, false) } else { fast_single::<R, FUSED>(x, y, false) };
128 r.unwrap_or_else(|| pow_any::<R, FUSED>(x, y))
129}
130
131#[inline]
133pub(crate) fn root<R: Native, const FUSED: bool>(n: R, y: R) -> R {
134 if let Some(r) = root_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, n, y) { return r; }
135 let r = if R::WIDE { fast::<R, FUSED>(y, n, true) } else { fast_single::<R, FUSED>(y, n, true) };
136 r.unwrap_or_else(|| root_any::<R, FUSED>(n, y))
137}
138
139pub(crate) const MULTIPLIED: u32 = 8;
141
142pub(crate) const COMPENSATED: u32 = 63;
144
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
147pub enum Exponent {
148 Whole { m: u32, negative: bool },
150 Halves { m: u32, negative: bool },
152 General,
154}
155
156impl Exponent {
157 #[inline(always)]
159 pub fn of(y: f64, fused: bool) -> Self {
160 let (a, negative) = (y.abs(), y < 0.0);
161 let most = if fused { COMPENSATED } else if negative { 1 } else { MULTIPLIED };
163 let m = a.max(1.0).min(most.into()).trunc();
164 if m == a { return Self::Whole { m: m as u32, negative }; }
165 let halves = a + a;
166 if (halves == 1.0) | ((halves == 3.0) & (!negative | fused)) { Self::Halves { m: halves as u32, negative } } else { Self::General }
167 }
168}
169
170#[inline(always)]
172fn each<F: crate::Float>(xs: &[F], out: &mut [std::mem::MaybeUninit<F>], f: impl Fn(F::Working) -> F::Working) {
173 for (r, &x) in out.iter_mut().zip(xs) { r.write(F::from_working(f(F::working(x)))); }
174}
175
176#[inline]
178fn general_slice<F: crate::Float>(x: crate::Arg<'_, F>, y: crate::Arg<'_, F>, n: usize, out: &mut [std::mem::MaybeUninit<F>], divide: bool) -> bool {
179 crate::slice::two_pass(
180 [x, y], n, out, #[inline(always)] |[a, b]| fast::<F::Working, { crate::FUSED }>(a.working(), b.working(), divide).map(F::from_working),
181 #[inline(always)] |[a, b]| {
182 F::from_working(if divide { root_any::<F::Working, { crate::FUSED }>(b.working(), a.working()) }
183 else { pow_any::<F::Working, { crate::FUSED }>(a.working(), b.working()) })
184 })
185}
186
187pub(crate) fn powers<F: crate::Float>(e: Exponent, k: F, xs: &[F], out: &mut [std::mem::MaybeUninit<F>]) {
189 #[inline(always)]
190 fn route<F: crate::Float>(e: Exponent, xs: &[F], out: &mut [std::mem::MaybeUninit<F>]) {
191 let o = crate::recipe::NativeOps::<{ crate::FUSED }>::DEFAULT;
192 each(xs, out, #[inline(always)] |x| crate::recipe::power(&o, e, x).expect("a route off the general route"));
193 }
194 macro_rules! routes { ($($kind:ident $m:literal),+) => { match e {
195 $(Exponent::$kind { m: $m, negative: false } => route(Exponent::$kind { m: $m, negative: false }, xs, out),
196 Exponent::$kind { m: $m, negative: true } => route(Exponent::$kind { m: $m, negative: true }, xs, out),)+
197 Exponent::General => { general_slice(crate::Arg::Many(xs), crate::Arg::One(k), xs.len(), out, false); }
198 e => route(e, xs, out),
199 } } }
200 routes!(Whole 1, Whole 2, Whole 3, Whole 4, Whole 5, Whole 6, Whole 7, Whole 8, Halves 1, Halves 3);
201}
202
203pub(crate) fn power_slice<F: crate::float::Native>(_: &Pow, [x, y]: [crate::Arg<'_, F>; 2], n: usize, out: &mut [std::mem::MaybeUninit<F>]) -> bool {
205 if let (crate::Arg::Many(xs), crate::Arg::One(k)) = (x, y) {
206 let e = Exponent::of(k.wide(), crate::FUSED);
207 powers(e, k, &xs[..n], &mut out[..n]);
208 return !matches!(e, Exponent::Halves { .. } | Exponent::General) || k.is_nan()
209 || xs[..n].iter().zip(unsafe { crate::slice::written(&mut out[..n]) }.iter()).all(|(x, r)| !r.is_nan() || x.is_nan());
210 }
211 general_slice(x, y, n, out, false)
212}
213
214pub(crate) fn roots<F: crate::Float>(n: F, ys: &[F], out: &mut [std::mem::MaybeUninit<F>]) {
216 let o = crate::recipe::NativeOps::<{ crate::FUSED }>::DEFAULT;
217 macro_rules! routes { ($($d:ident),+) => { match (Degree::of(n.wide()), n < F::zero()) {
218 $((Degree::$d, false) => each(ys, out, #[inline(always)] |y| rooted(&o, Degree::$d, false, y).expect("a degree off the general route")),
219 (Degree::$d, true) => each(ys, out, #[inline(always)] |y| rooted(&o, Degree::$d, true, y).expect("a degree off the general route")),)+
220 (Degree::General, _) => { general_slice(crate::Arg::Many(ys), crate::Arg::One(n), ys.len(), out, true); }
221 } } }
222 routes!(One, Two, Three, Four, Eight, Half);
223}
224
225pub(crate) fn root_slice<F: crate::float::Native>(_: &Root, [n, y]: [crate::Arg<'_, F>; 2], len: usize, out: &mut [std::mem::MaybeUninit<F>]) -> bool {
227 if let (crate::Arg::One(k), crate::Arg::Many(ys)) = (n, y) {
228 roots(k, &ys[..len], &mut out[..len]);
229 return matches!(Degree::of(k.wide()), Degree::One | Degree::Three) || k.is_nan()
230 || ys[..len].iter().zip(unsafe { crate::slice::written(&mut out[..len]) }.iter()).all(|(y, r)| !r.is_nan() || y.is_nan());
231 }
232 let o = crate::recipe::NativeOps::<{ crate::FUSED }>::DEFAULT;
233 general_slice(y, n, len, out, true);
234 let results = unsafe { crate::slice::written(&mut out[..len]) };
235 for (i, r) in results.iter_mut().enumerate() { if let Some(exact) = root_formula(&o, n.nth(i), y.nth(i)) { *r = exact; } }
236 results.iter().enumerate().all(|(i, r)| !r.is_nan() || n.nth(i).is_nan() || y.nth(i).is_nan())
237}
238
239#[derive(Clone, Copy, Debug)]
240pub struct Pow;
241#[derive(Clone, Copy, Debug)]
242pub struct RealPow;
243
244pub(crate) fn real_formula<F: crate::Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V) -> O::V {
245 let r = formula(o, o.magnitude(x), y);
246 let real = o.multiply(r, crate::recipe::sincospi(o, y).1);
247 o.select(o.less(x, o.number(F::zero())), real, r)
248}
249
250#[derive(Clone, Copy, Debug)]
252pub struct Root;
253
254const CBRT_MAGIC: [f64; 2] = [1499661097332631.0, 2773273.0];
256
257#[inline(always)]
259pub(crate) fn cbrt<R: Native, const FUSED: bool>(x: R) -> R { cbrt_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x) }
260
261#[inline]
262pub(crate) fn cbrt_formula<F: crate::Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
263 use crate::{buffer::FloatWidth, recipe::Integer};
264 let c = |x| o.number(F::narrow(x));
265 let wide = o.width() == FloatWidth::F64;
266 let (mantissa, bits, least, up, down) =
267 if wide { (52, 64, f64::MIN_POSITIVE, 18014398509481984.0, 3.814697265625e-6) } else { (23, 32, f32::MIN_POSITIVE as f64, 16777216.0, 0.00390625) };
268 let a = o.magnitude(x);
269 let sub = o.less(a, c(least));
270 let b = o.select(sub, o.multiply(a, c(up)), a);
271 let top = o.from_integer(o.integer_op(Integer::ShiftRight, o.to_bits(b), o.integer(bits - mantissa)));
272 let t = o.multiply_add(top, c(-1.0 / 3.0), c(CBRT_MAGIC[usize::from(!wide)]));
273 let rounded = o.to_bits(o.add(t, c((1u64 << mantissa) as f64)));
274 let rounded = o.integer_op(Integer::And, rounded, o.integer((1i64 << mantissa) - 1));
275 let mut z = o.from_bits(o.integer_op(Integer::ShiftLeft, rounded, o.integer(bits - mantissa)));
276 for _ in 0..if wide { 3 } else { 2 } { z = o.multiply(z, o.multiply_add(o.multiply(o.multiply(b, z), o.multiply(z, z)), c(-1.0 / 3.0), c(4.0 / 3.0))); }
277 let y = o.multiply(o.multiply(b, z), z);
278 let e = if o.fused() { o.multiply_add(y, z, c(-1.0)) } else {
279 let mask = o.integer(!((1i64 << ((mantissa + 2) / 2)) - 1));
280 let yh = o.from_bits(o.integer_op(Integer::And, o.to_bits(y), mask));
281 let zh = o.from_bits(o.integer_op(Integer::And, o.to_bits(z), mask));
282 let (yl, zl) = (o.subtract(y, yh), o.subtract(z, zh));
283 let p = o.multiply(y, z);
284 let error = o.add(o.add(o.add(o.subtract(o.multiply(yh, zh), p), o.multiply(yl, zh)), o.multiply(yh, zl)), o.multiply(yl, zl));
285 o.add(o.subtract(p, c(1.0)), error)
286 };
287 let y = o.multiply(o.multiply_add(o.multiply(y, c(-2.0 / 3.0)), e, y), o.select(sub, c(down), c(1.0)));
288 let finite = o.and(o.less(c(0.0), a), o.less(a, c(f64::INFINITY)));
289 let y = o.select(finite, y, a);
290 let sign = o.integer_op(Integer::And, o.to_bits(x), o.integer(if wide { i64::MIN } else { i32::MIN as u32 as i64 }));
291 o.from_bits(o.integer_op(Integer::Or, o.to_bits(y), sign))
292}
293
294#[inline]
296pub(crate) fn hypot<R: Native, const FUSED: bool>(x: R, y: R) -> R {
297 let (a, b) = (x.abs(), y.abs());
298 let (s, back) = (if a > b { a } else { b }).scales();
299 let (sa, sb) = (a * s, b * s);
300 let r = sa.mla::<FUSED>(sa, sb * sb).sqrt() * back;
301 if a.is_infinite() | b.is_infinite() { R::infinity() } else { r }
302}