rustymath_core/parts.rs
1//! The magnitude, sign and angle of a number, as function objects.
2use crate::slice::Total;
3use num_complex::{Complex32, Complex64};
4
5/// |x|, or the magnitude |z| of a complex z, which is real, as a function object.
6#[derive(Clone, Copy, Debug)]
7pub struct Abs;
8/// The sign of x as -1 or 1, or z/|z| for a complex z, as a function object. A zero gives itself, and NaN gives NaN.
9#[derive(Clone, Copy, Debug)]
10pub struct Sign;
11/// The angle from the positive real axis, from -π to π, as a function object. It is real for complex items too. A real x has angle 0, or π where its sign bit is set, as the angle of x + 0i is.
12#[derive(Clone, Copy, Debug)]
13pub struct Angle;
14
15macro_rules! objects {
16 ($($(#[doc = $doc:literal])+ $name:ident;)+) => {$(
17 $(#[doc = $doc])+
18 #[derive(Clone, Copy, Debug)]
19 pub struct $name;
20 )+};
21}
22objects! {
23 /// Whether a number has a NaN part.
24 Isnan;
25 /// Whether every part of a number is finite.
26 Isfinite;
27 /// Whether a number has an infinite part.
28 Isinf;
29 /// The real part of a number, with a real number unchanged.
30 Real;
31 /// The imaginary part of a number, zero for a real number.
32 Imag;
33 /// The complex conjugate, with a real number unchanged.
34 Conj;
35 /// Rounding toward zero, with whole numbers unchanged.
36 Trunc;
37 /// Rounding to the nearest whole number, with ties to even.
38 Rint;
39 /// Clipping x between lower and upper bounds in one pass: `minimum(upper, maximum(lower, x))`. A lower bound above the upper one gives the upper. A NaN argument gives NaN, x or a bound, depending on the target.
40 Clip;
41 /// `x` where `m` is nonzero and `y` where it's zero, item by item: a select, with the mask `m` as lanes of the items' type.
42 Choose;
43}
44
45/// Multiplication by a fixed real scale, computing f16 in f32 before rounding once.
46#[derive(Clone, Copy, Debug)]
47pub struct Scale(pub f64);
48
49/// Decimal rounding to nearest, ties to even. Constructing it precomputes the scale; extreme negative/positive precisions give zero/leave inputs unchanged.
50#[derive(Clone, Copy, Debug)]
51pub struct Round { pub(crate) scale: f64 }
52impl Round {
53 /// Rounds to this many digits after the decimal point; negative values round to tens, hundreds and so on.
54 pub fn new(decimals: i32) -> Self { Self { scale: 10f64.powi(decimals) } }
55}
56
57#[inline(always)]
58pub(crate) fn rounded<F: crate::Float>(x: F, scale: f64) -> F {
59 let scale = F::narrow(scale);
60 let scaled = x * scale;
61 if !x.is_finite() || scale.is_infinite() || scaled.is_infinite() { x }
62 else if scale == F::zero() { x * F::zero() }
63 else { scaled.round_ties_even() / scale }
64}
65
66/// `x` rounded to the nearest multiple of 1 ÷ `scale`, ties to even, exactly at `i128`, where no `i64` rounds past the type.
67#[inline(always)]
68pub(crate) fn rounded_wide(x: i128, scale: f64) -> i128 {
69 if scale >= 1.0 { return x; }
70 let scale = (1.0 / scale).round() as i128;
71 let (q, rem) = (x / scale, x % scale);
72 let away = rem.abs() * 2;
73 let q = q + if away > scale || away == scale && q & 1 != 0 { x.signum() } else { 0 };
74 q * scale
75}
76
77forms!(Choose at real: |_, [x, y, m]| Total(if m != 0.0 { x } else { y }));
78forms!(Choose at Complex32, Complex64: |_, [x, y, m]| Total(if m.re != 0.0 { x } else { y }));
79forms!(Choose at u8, i16, i32, i64: |_, [x, y, m]| if m != 0 { x } else { y });
80forms!(Choose at bool: |_, [x, y, m]| (m & x) | (!m & y));
81forms!(Choose at char: |_, [x, y, m]| if m == '\u{1}' { x } else { y });