1use crate::{Exponent, Float, float::sealed::Core, tolerant::Band};
3macro_rules! u64_integers {
5 ($bits:expr) => {
6 #[inline(always)]
7 fn integer(&self, x: i64) -> u64 {
8 x as u64
9 }
10 #[inline(always)]
11 fn integer_op(&self, op: Integer, x: u64, y: u64) -> u64 {
12 crate::recipe::integer_op(op, x, y, $bits)
13 }
14 #[inline(always)]
15 fn integer_compare(&self, op: Cmp, x: u64, y: u64) -> bool {
16 crate::recipe::integer_compare(op, x, y)
17 }
18 #[inline(always)]
19 fn integer_select(&self, test: bool, x: u64, y: u64) -> u64 {
20 if test { x } else { y }
21 }
22 };
23}
24macro_rules! plain_logic {
26 ($t:ty) => {
27 #[inline(always)]
28 fn compare(&self, c: Cmp, x: $t, y: $t) -> bool {
29 match c {
30 Cmp::Less => x < y,
31 Cmp::LessEqual => x <= y,
32 Cmp::Equal => x == y,
33 }
34 }
35 #[inline(always)]
36 fn and(&self, x: bool, y: bool) -> bool {
37 x & y
38 }
39 #[inline(always)]
40 fn or(&self, x: bool, y: bool) -> bool {
41 x | y
42 }
43 #[inline(always)]
44 fn not(&self, x: bool) -> bool {
45 !x
46 }
47 #[inline(always)]
48 fn select(&self, test: bool, x: $t, y: $t) -> $t {
49 if test { x } else { y }
50 }
51 };
52}
53mod integer;
54pub use crate::arith::Overflowing;
55pub use integer::IntegerOps;
56
57#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
59pub enum Unary {
60 Negate,
61 Magnitude,
62 Sqrt,
63 Floor,
64 Ln,
65 Sin,
66 Cos,
67 Sinh,
68 Cosh,
69 Sqrt1mx2,
71 Sqrt1px2,
73 Digamma,
75 Exp,
77 Lgamma,
79 Not,
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
85pub enum Binary {
86 Add,
87 Subtract,
88 Multiply,
89 Divide,
90 Power,
92 Larger,
94 Smaller,
96 Binom,
98 BinomDn,
100 BinomDk,
102 Polygamma,
104 BitAnd,
106}
107
108#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
110pub enum Cmp { Less, LessEqual, Equal }
111
112#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
113pub enum Integer { Add, Subtract, And, Or, Xor, ShiftLeft, ShiftRight, SignedShiftRight }
114
115#[inline(always)]
117pub(crate) fn integer_op(op: Integer, x: u64, y: u64, bits: u32) -> u64 {
118 match op {
119 Integer::Add => x.wrapping_add(y), Integer::Subtract => x.wrapping_sub(y), Integer::And => x & y, Integer::Or => x | y, Integer::Xor => x ^ y,
120 Integer::ShiftLeft => shifted(x, y as i64), Integer::ShiftRight => { if y >= 64 { 0 } else { (x & (u64::MAX >> (64 - bits))) >> y } }
121 Integer::SignedShiftRight => shifted(x, (y as i64).saturating_neg())
122 }
123}
124
125#[inline(always)]
127pub(crate) fn integer_compare(op: Cmp, x: u64, y: u64) -> bool {
128 match op { Cmp::Less => (x as i64) < y as i64, Cmp::LessEqual => (x as i64) <= y as i64, Cmp::Equal => x == y }
129}
130
131#[inline(always)]
133fn shifted(x: u64, count: i64) -> u64 { if count >= 64 { 0 } else if count >= 0 { x << count } else { ((x as i64) >> count.unsigned_abs().min(63)) as u64 } }
134
135macro_rules! named {
137 (unary: $($u:ident $uid:ident),+; binary: $($b:ident $bid:ident),+; compare: $($c:ident $cid:ident),+) => {
138 $( #[inline] fn $u(&self, x: Self::V) -> Self::V { self.unary(Unary::$uid, x) } )+
139 $( #[inline] fn $b(&self, x: Self::V, y: Self::V) -> Self::V { self.binary(Binary::$bid, x, y) } )+
140 $( #[inline] fn $c(&self, x: Self::V, y: Self::V) -> Self::B { self.compare(Cmp::$cid, x, y) } )+
141 };
142}
143
144pub trait Ops<F: Float> {
146 type V: Copy;
148 type B: Copy;
150 type I: Copy;
152 fn width(&self) -> crate::buffer::FloatWidth;
153 fn fused(&self) -> bool;
154 type W: Copy;
156 fn widen(&self, x: Self::V) -> Self::W;
157 fn wide_number(&self, x: f64) -> Self::W;
158 fn wide_multiply_add(&self, x: Self::W, y: Self::W, z: Self::W) -> Self::W;
159 fn narrow(&self, x: Self::W) -> Self::V;
160 fn integer(&self, x: i64) -> Self::I;
161 fn integer_op(&self, op: Integer, x: Self::I, y: Self::I) -> Self::I;
162 fn integer_compare(&self, op: Cmp, x: Self::I, y: Self::I) -> Self::B;
163 fn integer_select(&self, test: Self::B, x: Self::I, y: Self::I) -> Self::I;
164 fn to_integer(&self, x: Self::V) -> Self::I;
165 fn from_integer(&self, x: Self::I) -> Self::V;
166 fn to_bits(&self, x: Self::V) -> Self::I;
167 fn from_bits(&self, x: Self::I) -> Self::V;
168 fn number(&self, x: F) -> Self::V;
170 fn unary(&self, op: Unary, x: Self::V) -> Self::V;
171 fn binary(&self, op: Binary, x: Self::V, y: Self::V) -> Self::V;
172 fn compare(&self, c: Cmp, x: Self::V, y: Self::V) -> Self::B;
173 fn multiply_add(&self, x: Self::V, y: Self::V, z: Self::V) -> Self::V;
175 fn overflow(&self, op: Binary, x: Self::V, y: Self::V) -> Self::B;
176 fn and(&self, x: Self::B, y: Self::B) -> Self::B;
177 fn or(&self, x: Self::B, y: Self::B) -> Self::B;
178 fn not(&self, x: Self::B) -> Self::B;
179 fn select(&self, m: Self::B, x: Self::V, y: Self::V) -> Self::V;
181 #[inline]
183 fn one_where(&self, m: Self::B) -> Self::V { self.select(m, self.number(F::one()), self.number(F::zero())) }
184 #[inline]
186 fn constant(&self, _v: Self::V) -> Option<F> { None }
187 #[inline]
189 fn integral(&self) -> bool { false }
190 #[inline]
192 fn tolerance(&self, fallback: F) -> Self::V { self.number(fallback) }
193 #[inline]
195 fn sign(&self, x: Self::V) -> Self::V { sign(self, x) }
196 #[inline]
198 fn reciprocal(&self, x: Self::V) -> Self::V { reciprocal(self, x) }
199 #[inline]
201 fn power(&self, k: F, x: Self::V) -> Self::V { power(self, Exponent::of(k.wide(), self.fused()), x).expect("an exponent off the general route") }
202 #[inline]
204 fn real_power(&self, x: Self::V, y: Self::V) -> Self::V { crate::pow::real_formula(self, x, y) }
205 #[inline]
207 fn power_slope(&self, x: Self::V, y: Self::V, r: Self::V, halves: bool) -> Self::V { crate::derivative::power_slope(self, x, y, r, halves) }
208 named! {
209 unary: negate Negate, magnitude Magnitude, sqrt Sqrt, floor Floor, ln Ln, sin Sin, cos Cos, sinh Sinh, cosh Cosh, sqrt1mx2 Sqrt1mx2, sqrt1px2 Sqrt1px2, digamma Digamma, exp Exp, lgamma Lgamma, complement Not;
210 binary: add Add, subtract Subtract, multiply Multiply, divide Divide, larger Larger, smaller Smaller, binom Binom, binom_dn BinomDn, binom_dk BinomDk, polygamma Polygamma, bit_and BitAnd;
211 compare: less Less, less_equal LessEqual, equal Equal
212 }
213}
214
215pub struct RangeOps(pub crate::buffer::DType);
217impl RangeOps {
218 fn compare(op: Cmp, x: crate::domain::Range, y: crate::domain::Range) -> Option<bool> {
219 if x.is_empty() || y.is_empty() { return None; }
220 match op {
221 Cmp::Less if x.hi < y.lo => Some(true), Cmp::Less if x.lo >= y.hi => Some(false), Cmp::LessEqual if x.hi <= y.lo => Some(true),
222 Cmp::LessEqual if x.lo > y.hi => Some(false), Cmp::Equal if x.constant().is_some() && x == y => Some(true),
223 Cmp::Equal if x.hi < y.lo || y.hi < x.lo => Some(false), _ => None
224 }
225 }
226 fn selected(test: Option<bool>, x: crate::domain::Range, y: crate::domain::Range) -> crate::domain::Range {
227 match test { Some(true) => x, Some(false) => y, None => x.join(y) }
228 }
229}
230impl<F: Float> Ops<F> for RangeOps {
231 type V = crate::domain::Range;
232 type B = Option<bool>;
233 type I = crate::domain::Range;
234 type W = crate::domain::Range;
235 fn width(&self) -> crate::buffer::FloatWidth { self.0.numpy_float() }
236 fn fused(&self) -> bool { crate::FUSED }
237 fn integral(&self) -> bool { self.0.is_integral() }
238 fn widen(&self, _: Self::V) -> Self::W { crate::domain::Range::ANY }
239 fn wide_number(&self, x: f64) -> Self::W { crate::domain::Range::of(Some(x)) }
240 fn wide_multiply_add(&self, _: Self::W, _: Self::W, _: Self::W) -> Self::W { crate::domain::Range::ANY }
241 fn narrow(&self, _: Self::W) -> Self::V { crate::domain::Range::ANY }
242 fn integer(&self, x: i64) -> Self::I { crate::domain::Range::of((x.unsigned_abs() <= 1 << 53).then_some(x as f64)) }
243 fn integer_op(&self, _: Integer, _: Self::I, _: Self::I) -> Self::I { crate::domain::Range::ANY }
244 fn integer_compare(&self, op: Cmp, x: Self::I, y: Self::I) -> Self::B { Self::compare(op, x, y) }
245 fn integer_select(&self, test: Self::B, x: Self::I, y: Self::I) -> Self::I { Self::selected(test, x, y) }
246 fn to_integer(&self, _: Self::V) -> Self::I { crate::domain::Range::ANY }
247 fn from_integer(&self, _: Self::I) -> Self::V { crate::domain::Range::ANY }
248 fn to_bits(&self, _: Self::V) -> Self::I { crate::domain::Range::ANY }
249 fn from_bits(&self, _: Self::I) -> Self::V { crate::domain::Range::ANY }
250 fn number(&self, x: F) -> Self::V { crate::domain::Range::of(Some(x.wide())) }
251 fn constant(&self, x: Self::V) -> Option<F> { x.constant().map(F::narrow) }
252 fn unary(&self, op: Unary, x: Self::V) -> Self::V { if op == Unary::Magnitude { x.magnitude() } else { crate::domain::Range::ANY } }
253 fn binary(&self, _: Binary, _: Self::V, _: Self::V) -> Self::V { crate::domain::Range::ANY }
254 fn compare(&self, op: Cmp, x: Self::V, y: Self::V) -> Self::B { Self::compare(op, x, y) }
255 fn multiply_add(&self, _: Self::V, _: Self::V, _: Self::V) -> Self::V { crate::domain::Range::ANY }
256 fn overflow(&self, op: Binary, x: Self::V, y: Self::V) -> Self::B {
258 use crate::facts::Formula;
259 let result = match op {
260 Binary::Add => crate::Add.range([x, y]), Binary::Subtract => crate::Subtract.range([x, y]), Binary::Multiply => crate::Multiply.range([x, y]),
261 _ => return None
262 };
263 let fits = crate::domain::Range::of_dtype(self.0);
264 let exact = |bound: f64| bound.abs() < 2f64.powi(53);
265 let above = if exact(fits.lo) { result.lo >= fits.lo } else { result.lo > fits.lo };
266 let below = if exact(fits.hi) { result.hi <= fits.hi } else { result.hi < fits.hi };
267 (above && below).then_some(false)
268 }
269 fn and(&self, x: Self::B, y: Self::B) -> Self::B {
270 match (x, y) { (Some(false), _) | (_, Some(false)) => Some(false), (Some(true), Some(true)) => Some(true), _ => None }
271 }
272 fn or(&self, x: Self::B, y: Self::B) -> Self::B {
273 match (x, y) { (Some(true), _) | (_, Some(true)) => Some(true), (Some(false), Some(false)) => Some(false), _ => None }
274 }
275 fn not(&self, x: Self::B) -> Self::B { x.map(|x| !x) }
276 fn select(&self, test: Self::B, x: Self::V, y: Self::V) -> Self::V { Self::selected(test, x, y) }
277}
278
279#[derive(Clone, Copy, Debug, Default)]
281pub struct NativeOps<const FUSED: bool> { pub tolerance: Option<f64> }
282impl<const FUSED: bool> NativeOps<FUSED> { pub const DEFAULT: Self = Self { tolerance: None }; }
283pub type Floats = NativeOps<{ crate::FUSED }>;
284#[allow(non_upper_case_globals)]
285pub const Floats: Floats = NativeOps::DEFAULT;
286
287impl<F: Float, const FUSED: bool> Ops<F> for NativeOps<FUSED> {
288 type V = F;
289 type B = bool;
290 type I = u64;
291 type W = f64;
292 #[inline(always)]
293 fn fused(&self) -> bool { FUSED }
294 #[inline(always)]
295 fn widen(&self, x: F) -> f64 { x.wide() }
296 #[inline(always)]
297 fn wide_number(&self, x: f64) -> f64 { x }
298 #[inline(always)]
299 fn wide_multiply_add(&self, x: f64, y: f64, z: f64) -> f64 { if FUSED { x.mul_add(y, z) } else { x * y + z } }
300 #[inline(always)]
301 fn narrow(&self, x: f64) -> F { F::narrow(x) }
302 #[inline(always)]
303 fn width(&self) -> crate::buffer::FloatWidth { if F::Working::WIDE { crate::buffer::FloatWidth::F64 } else { crate::buffer::FloatWidth::F32 } }
304 u64_integers!(64);
305 #[inline(always)]
306 fn to_integer(&self, x: F) -> u64 { (x.wide() as i64) as u64 }
307 #[inline(always)]
308 fn from_integer(&self, x: u64) -> F { F::narrow((x as i64) as f64) }
309 #[inline(always)]
310 fn to_bits(&self, x: F) -> u64 { if F::Working::WIDE { x.wide().to_bits() } else { (x.wide() as f32).to_bits() as u64 } }
311 #[inline(always)]
312 fn from_bits(&self, x: u64) -> F { F::narrow(if F::Working::WIDE { f64::from_bits(x) } else { f32::from_bits(x as u32) as f64 }) }
313 #[inline(always)]
314 fn number(&self, x: F) -> F { x }
315 #[inline(always)]
316 fn unary(&self, op: Unary, x: F) -> F {
317 match op {
318 Unary::Negate => -x, Unary::Magnitude => x.abs(), Unary::Sqrt => x.sqrt(), Unary::Floor => x.floor(), Unary::Ln => crate::ln(x),
319 Unary::Sin => crate::sin(x), Unary::Cos => crate::cos(x), Unary::Sinh => crate::sinh(x), Unary::Cosh => crate::cosh(x),
320 Unary::Sqrt1mx2 => crate::sqrt1mx2(x), Unary::Sqrt1px2 => crate::sqrt1px2(x), Unary::Digamma => crate::digamma(x), Unary::Exp => crate::exp(x),
321 Unary::Lgamma => crate::lgamma(x), Unary::Not => F::one() - x
322 }
323 }
324 #[inline(always)]
325 fn binary(&self, op: Binary, x: F, y: F) -> F {
326 match op {
327 Binary::Add => x + y, Binary::Subtract => x - y, Binary::Multiply => x * y, Binary::Divide => x / y, Binary::Larger => { if x > y { x } else { y } }
328 Binary::Smaller => { if x < y { x } else { y } } Binary::Binom => crate::binom(x, y), Binary::BinomDn => crate::binom_dn(x, y),
329 Binary::Power => F::from_working(crate::pow::pow::<F::Working, FUSED>(x.working(), y.working())), Binary::BinomDk => crate::binom_dk(x, y),
330 Binary::Polygamma => crate::polygamma(x, y),
331 Binary::BitAnd => unreachable!("floats take no bitwise and: a recipe asks for one only where `Ops::integral` holds")
332 }
333 }
334 plain_logic!(F);
335 #[inline(always)]
336 fn multiply_add(&self, x: F, y: F, z: F) -> F { F::from_working(x.working().mla::<FUSED>(y.working(), z.working())) }
337 #[inline(always)]
338 fn overflow(&self, _op: Binary, _x: F, _y: F) -> bool { false }
339 #[inline(always)]
340 fn constant(&self, value: F) -> Option<F> { Some(value) }
341 #[inline(always)]
342 fn tolerance(&self, fallback: F) -> F { self.tolerance.map_or(fallback, F::narrow) }
343}
344
345#[inline(always)]
347pub fn close<F: Float, O: Ops<F> + ?Sized>(o: &O, t: O::V, x: O::V, y: O::V) -> O::B {
348 let gap = o.subtract(x, y);
349 let gap = o.magnitude(gap);
350 let (a, b) = (o.magnitude(x), o.magnitude(y));
351 let size = o.larger(a, b);
352 let bound = o.multiply(t, size);
353 o.less(gap, bound)
354}
355
356#[inline(always)]
358pub fn equal<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V, close: impl FnOnce(&O) -> O::B) -> O::B {
359 if o.integral() { return o.equal(x, y); }
360 let same = o.equal(x, y);
361 let close = close(o);
362 o.or(same, close)
363}
364
365#[inline(always)]
367pub fn less<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V, close: impl FnOnce(&O) -> O::B) -> O::B {
368 if o.integral() { return o.less(x, y); }
369 let below = o.less(x, y);
370 let close = close(o);
371 let apart = o.not(close);
372 o.and(below, apart)
373}
374
375#[inline(always)]
377pub fn less_equal<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V, close: impl FnOnce(&O) -> O::B) -> O::B {
378 if o.integral() { return o.less_equal(x, y); }
379 let below = o.less_equal(x, y);
380 let close = close(o);
381 o.or(below, close)
382}
383
384#[inline(always)]
386pub fn floor<F: Float, O: Ops<F> + ?Sized>(o: &O, t: O::V, y: O::V) -> O::V {
387 if o.integral() { return y; }
388 let size = o.magnitude(y);
389 let raise = o.multiply(t, size);
390 let most = o.number(F::narrow(0.5).next_down());
391 let raise = o.smaller(raise, most);
392 let raised = o.add(y, raise);
393 o.floor(raised)
394}
395
396#[inline(always)]
397pub(crate) fn integer_floor_divide<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V) -> O::V {
398 let q = o.divide(x, y);
399 let r = o.subtract(x, o.multiply(q, y));
400 let zero = o.number(F::zero());
401 let lower = o.or(o.and(o.less(r, zero), o.less(zero, y)), o.and(o.less(zero, r), o.less(y, zero)));
402 o.subtract(q, o.one_where(lower))
403}
404
405#[inline(always)]
407pub fn exact_floor<F: Float, O: Ops<F> + ?Sized>(o: &O, y: O::V) -> O::V {
408 let whole = o.floor(y);
409 let zero = o.number(F::zero());
410 o.add(whole, zero)
411}
412
413#[inline(always)]
415pub fn ceil<F: Float, O: Ops<F> + ?Sized>(o: &O, y: O::V, floor: impl FnOnce(&O, O::V) -> O::V) -> O::V {
416 if o.integral() { return y; }
417 let negated = o.negate(y);
418 let whole = floor(o, negated);
419 let zero = o.number(F::zero());
420 o.subtract(zero, whole)
421}
422
423#[inline(always)]
425pub fn residue<F: Float, O: Ops<F> + ?Sized>(o: &O, t: Option<O::V>, x: O::V, y: O::V, floor: impl FnOnce(&O, O::V) -> O::V) -> O::V {
426 let quotient = o.divide(y, x);
427 let q = floor(o, quotient);
428 let minus = o.negate(x);
429 let r = o.multiply_add(minus, q, y);
430 let r = match t {
431 Some(t) => {
432 let product = o.multiply(x, q);
433 let whole = equal(o, product, y, |o| close(o, t, product, y));
434 let zero = o.number(F::zero());
435 o.select(whole, zero, r)
436 }
437 None => r,
438 };
439 let zero = o.number(F::zero());
440 let none = o.equal(x, zero);
441 o.select(none, y, r)
442}
443
444#[inline(always)]
446pub fn root2<F: Float, O: Ops<F> + ?Sized>(o: &O, y: O::V) -> O::V {
447 let root = o.sqrt(y);
448 let zero = o.number(F::zero());
449 let root = o.add(root, zero);
450 let low = o.number(F::neg_infinity());
451 let infinite = o.equal(y, low);
452 let flipped = o.negate(y);
453 o.select(infinite, flipped, root)
454}
455
456#[inline(always)]
458pub fn real_sqrt<F: Float, O: Ops<F> + ?Sized>(o: &O, y: O::V) -> O::V {
459 let zero = o.number(F::zero());
460 let clamped = o.larger(zero, y);
461 o.sqrt(clamped)
462}
463
464#[inline(always)]
466pub fn sincospi<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> (O::V, O::V) {
467 let (half, quarter) = (o.number(F::narrow(0.5)), o.number(F::narrow(0.25)));
468 let n = o.floor(o.add(o.add(x, x), half));
469 let a = o.multiply(o.subtract(x, o.multiply(n, half)), o.number(F::narrow(core::f64::consts::PI)));
470 let (s, k) = (o.sin(a), o.cos(a));
471 let q = o.subtract(n, o.multiply(o.number(F::narrow(4.0)), o.floor(o.multiply(n, quarter))));
472 let [q0, q1, q2] = [0.0, 1.0, 2.0].map(|m| o.equal(q, o.number(F::narrow(m))));
473 let zero = o.number(F::zero());
475 let (ns, nk) = (o.subtract(zero, s), o.subtract(zero, k));
476 (o.select(q0, s, o.select(q1, k, o.select(q2, ns, nk))), o.select(q0, k, o.select(q1, ns, o.select(q2, nk, s))))
477}
478
479#[inline(always)]
481pub fn in_band<F: Float, O: Ops<F> + ?Sized>(o: &O, band: Band, lo: O::V, hi: O::V, v: O::V) -> O::B {
482 match band {
483 Band::Inside | Band::Outside => {
484 let (above, below) = (o.less_equal(lo, v), o.less_equal(v, hi));
485 let inside = o.and(above, below);
486 if matches!(band, Band::Outside) { o.not(inside) } else { inside }
487 }
488 Band::Below => o.less(v, lo),
489 Band::NotAbove => o.less_equal(v, hi),
490 Band::Above => o.less(hi, v),
491 Band::NotBelow => o.less_equal(lo, v),
492 }
493}
494
495#[inline(always)]
497pub fn reciprocal<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V { let one = o.number(F::one()); o.divide(one, x) }
498
499#[inline(always)]
501pub fn pi_times<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V { let pi = o.number(F::narrow(std::f64::consts::PI)); o.multiply(x, pi) }
502
503#[inline(always)]
505pub fn pi_ratio<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V) -> O::V { let ratio = o.divide(x, y); pi_times(o, ratio) }
506
507#[inline(always)]
509pub fn mathish_divide<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V) -> O::V {
510 let quotient = o.divide(x, y);
511 let zero = o.number(F::zero());
512 let (a, b) = (o.equal(x, zero), o.equal(y, zero));
513 let both = o.and(a, b);
514 o.select(both, x, quotient)
515}
516
517#[inline(always)]
519pub fn sign<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
520 let (zero, one, minus) = (o.number(F::zero()), o.number(F::one()), o.number(-F::one()));
521 let (above, below) = (o.less(zero, x), o.less(x, zero));
522 o.select(above, one, o.select(below, minus, x))
523}
524
525#[inline(always)]
527pub fn power<F: Float, O: Ops<F> + ?Sized>(o: &O, e: Exponent, x: O::V) -> Option<O::V> {
528 let (m, negative, x) = match e {
529 Exponent::Whole { m, negative } => (m, negative, x), Exponent::Halves { m, negative } => (m, negative, root2(o, x)), Exponent::General => return None
530 };
531 Some(if !o.integral() && (m > crate::pow::MULTIPLIED || (negative && m > 1)) { whole(o, m, negative, x) } else { multiplied(o, m, negative, x) })
532}
533
534#[inline(always)]
536fn multiplied<F: Float, O: Ops<F> + ?Sized>(o: &O, m: u32, negative: bool, x: O::V) -> O::V {
537 let (mut base, mut k) = (x, m);
538 while k & 1 == 0 {
539 base = o.multiply(base, base);
540 k >>= 1;
541 }
542 let mut r = base;
543 while k > 1 {
544 k >>= 1;
545 base = o.multiply(base, base);
546 if k & 1 == 1 { r = o.multiply(r, base); }
547 }
548 if negative { reciprocal(o, r) } else { r }
549}
550
551pub(crate) type Word<V> = (V, Option<V>);
553
554#[inline(always)]
556pub(crate) fn whole<F: Float, O: Ops<F> + ?Sized>(o: &O, m: u32, negative: bool, x: O::V) -> O::V {
557 let (b, mut error) = base(o, negative, x);
558 let mut w = (b, None);
559 for i in (0..u32::BITS - 1 - m.leading_zeros()).rev() { w = step(o, w, b, m, i, &mut error); }
560 w.0
561}
562
563#[inline(always)]
565pub(crate) fn base<F: Float, O: Ops<F> + ?Sized>(o: &O, negative: bool, x: O::V) -> (O::V, Option<O::V>) {
566 if !negative { return (x, None); }
567 let q = reciprocal(o, x);
568 let one = o.number(F::one());
569 (q, Some(o.multiply_add(o.negate(x), q, one)))
570}
571
572#[inline(always)]
574pub(crate) fn step<F: Float, O: Ops<F> + ?Sized>(o: &O, w: Word<O::V>, b: O::V, m: u32, i: u32, error: &mut Option<O::V>) -> Word<O::V> {
575 let w = times(o, w, None, m, i, error);
576 if (m >> i) & 1 == 1 { times(o, w, Some(b), m, i, error) } else { w }
577}
578
579#[inline(always)]
581fn times<F: Float, O: Ops<F> + ?Sized>(o: &O, (h, l): Word<O::V>, by: Option<O::V>, m: u32, i: u32, error: &mut Option<O::V>) -> Word<O::V> {
582 let y = by.unwrap_or(h);
583 let twice = |o: &O| by.unwrap_or_else(|| o.add(h, h));
585 if i > 0 {
586 let p = o.multiply(h, y);
587 let e = o.multiply_add(h, y, o.negate(p));
588 return (p, Some(match l { Some(l) => o.multiply_add(twice(o), l, e), None => e }));
589 }
590 let squares = i + by.is_none() as u32;
591 let fold = error.take().map(|e| o.multiply(e, o.number(F::narrow(m as f64 / (1u64 << squares) as f64))));
592 let l = match (l, fold) {
593 (None, None) => return (o.multiply(h, y), None), (Some(l), None) => l, (None, Some(c)) => o.multiply(h, c),
594 (Some(l), Some(c)) => o.multiply_add(h, c, l)
595 };
596 let k = o.multiply(twice(o), l);
597 let k = o.select(finite(o, k), k, o.number(F::zero()));
599 (o.multiply_add(h, y, k), None)
600}
601
602#[inline(always)]
604fn finite<F: Float, O: Ops<F> + ?Sized>(o: &O, v: O::V) -> O::B { o.equal(o.subtract(v, v), o.number(F::zero())) }
605
606#[inline(always)]
607pub fn horner<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, coefficients: &[f64]) -> O::V {
608 let mut result = o.number(F::narrow(coefficients[0]));
609 for &coefficient in &coefficients[1..] { result = o.multiply_add(result, x, o.number(F::narrow(coefficient))); }
610 result
611}
612
613#[inline(always)]
614pub fn ln2<F: Float, O: Ops<F> + ?Sized>(o: &O) -> (O::V, O::V) {
615 let (hi, lo) = if o.width() == crate::buffer::FloatWidth::F64 {
616 (0.69314718055966295651160180568695068359375, 0.28235290563031577122588448175013436025525412068e-12)
617 } else { (0.693145751953125, 1.428606765330187e-06) };
618 (o.number(F::narrow(hi)), o.number(F::narrow(lo)))
619}
620
621#[inline(always)]
622pub fn rounding_shift<F: Float, O: Ops<F> + ?Sized>(o: &O) -> O::V {
623 o.number(F::narrow(if o.width() == crate::buffer::FloatWidth::F64 { 6755399441055744.0 } else { 12582912.0 }))
624}
625
626#[inline(always)]
628pub fn pow2<F: Float, O: Ops<F> + ?Sized>(o: &O, z: O::V) -> O::V {
629 let (mantissa, bias) = if o.width() == crate::buffer::FloatWidth::F64 { (52, 1023) } else { (23, 127) };
630 let bits = o.integer_op(Integer::Add, o.to_bits(z), o.integer(bias));
631 o.from_bits(o.integer_op(Integer::ShiftLeft, bits, o.integer(mantissa)))
632}
633
634#[inline(always)]
636pub fn ldexp<F: Float, O: Ops<F> + ?Sized>(o: &O, y: O::V, n: O::V) -> O::V {
637 let shift = rounding_shift(o);
638 let half = o.multiply_add(n, o.number(F::narrow(0.5)), shift);
639 let rest = o.add(o.subtract(n, o.subtract(half, shift)), shift);
640 o.multiply(o.multiply(y, pow2(o, half)), pow2(o, rest))
641}
642
643#[inline(always)]
645pub fn split<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> (O::V, O::V) {
646 let (mantissa, bias) = if o.width() == crate::buffer::FloatWidth::F64 { (52, 1023) } else { (23, 127) };
647 let scaled = o.multiply(x, o.number(F::narrow(1.0 / 0.75)));
648 let exponent = o.integer_op(Integer::ShiftRight, o.to_bits(scaled), o.integer(mantissa));
649 let exponent = o.integer_op(Integer::And, exponent, o.integer(2 * bias + 1));
650 let unbiased = o.integer_op(Integer::Subtract, exponent, o.integer(bias));
651 let offset = o.integer_op(Integer::ShiftLeft, unbiased, o.integer(mantissa));
652 (o.from_bits(o.integer_op(Integer::Subtract, o.to_bits(x), offset)), o.from_integer(unbiased))
653}
654
655#[inline(always)]
657pub fn frexp<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> (O::V, O::V) {
658 let smallest = if o.width() == crate::buffer::FloatWidth::F64 { f64::MIN_POSITIVE } else { f32::MIN_POSITIVE as f64 };
659 let small = o.less(x, o.number(F::narrow(smallest)));
660 let scale = o.select(small, o.number(F::narrow(18446744073709551616.0)), o.number(F::one()));
661 let (m, e) = split(o, o.multiply(x, scale));
662 (m, o.subtract(e, o.select(small, o.number(F::narrow(64.0)), o.number(F::zero()))))
663}