Skip to main content

rustpython_common/
float_ops.rs

1use core::f64;
2use malachite_bigint::{BigInt, ToBigInt};
3use num_traits::{Float, Signed, ToPrimitive, Zero};
4
5pub const fn decompose_float(value: f64) -> (f64, i32) {
6    if 0.0 == value {
7        (0.0, 0i32)
8    } else {
9        let bits = value.to_bits();
10        let exponent: i32 = ((bits >> 52) & 0x7ff) as i32 - 1022;
11        let mantissa_bits = bits & (0x000f_ffff_ffff_ffff) | (1022 << 52);
12        (f64::from_bits(mantissa_bits), exponent)
13    }
14}
15
16/// Equate an integer to a float.
17///
18/// Returns true if and only if, when converted to each others types, both are equal.
19///
20/// # Examples
21///
22/// ```
23/// use malachite_bigint::BigInt;
24/// use rustpython_common::float_ops::eq_int;
25/// let a = 1.0f64;
26/// let b = BigInt::from(1);
27/// let c = 2.0f64;
28/// assert!(eq_int(a, &b));
29/// assert!(!eq_int(c, &b));
30/// ```
31///
32pub fn eq_int(value: f64, other: &BigInt) -> bool {
33    if let (Some(self_int), Some(other_float)) = (value.to_bigint(), other.to_f64()) {
34        value == other_float && self_int == *other
35    } else {
36        false
37    }
38}
39
40pub fn lt_int(value: f64, other_int: &BigInt) -> bool {
41    match (value.to_bigint(), other_int.to_f64()) {
42        (Some(self_int), Some(other_float)) => value < other_float || self_int < *other_int,
43        // finite float, other_int too big for float,
44        // the result depends only on other_int’s sign
45        (Some(_), None) => other_int.is_positive(),
46        // infinite float must be bigger or lower than any int, depending on its sign
47        _ if value.is_infinite() => value.is_sign_negative(),
48        // NaN, always false
49        _ => false,
50    }
51}
52
53pub fn gt_int(value: f64, other_int: &BigInt) -> bool {
54    match (value.to_bigint(), other_int.to_f64()) {
55        (Some(self_int), Some(other_float)) => value > other_float || self_int > *other_int,
56        // finite float, other_int too big for float,
57        // the result depends only on other_int’s sign
58        (Some(_), None) => other_int.is_negative(),
59        // infinite float must be bigger or lower than any int, depending on its sign
60        _ if value.is_infinite() => value.is_sign_positive(),
61        // NaN, always false
62        _ => false,
63    }
64}
65
66pub const fn div(v1: f64, v2: f64) -> Option<f64> {
67    if v2 != 0.0 { Some(v1 / v2) } else { None }
68}
69
70pub fn mod_(v1: f64, v2: f64) -> Option<f64> {
71    if v2 != 0.0 {
72        let val = v1 % v2;
73        match (v1.signum() as i32, v2.signum() as i32) {
74            (1, 1) | (-1, -1) => Some(val),
75            _ if (v1 == 0.0) || (v1.abs() == v2.abs()) => Some(val.copysign(v2)),
76            _ => Some((val + v2).copysign(v2)),
77        }
78    } else {
79        None
80    }
81}
82
83pub fn floordiv(v1: f64, v2: f64) -> Option<f64> {
84    if v2 != 0.0 {
85        Some((v1 / v2).floor())
86    } else {
87        None
88    }
89}
90
91pub fn divmod(v1: f64, v2: f64) -> Option<(f64, f64)> {
92    if v2 != 0.0 {
93        let mut m = v1 % v2;
94        let mut d = (v1 - m) / v2;
95        if v2.is_sign_negative() != m.is_sign_negative() {
96            m += v2;
97            d -= 1.0;
98        }
99        Some((d, m))
100    } else {
101        None
102    }
103}
104
105// nextafter algorithm based off of https://gitlab.com/bronsonbdevost/next_afterf
106#[allow(clippy::float_cmp)]
107pub fn nextafter(x: f64, y: f64) -> f64 {
108    if x == y {
109        y
110    } else if x.is_nan() || y.is_nan() {
111        f64::NAN
112    } else if x >= f64::INFINITY {
113        f64::MAX
114    } else if x <= f64::NEG_INFINITY {
115        f64::MIN
116    } else if x == 0.0 {
117        f64::from_bits(1).copysign(y)
118    } else {
119        // next x after 0 if y is farther from 0 than x, otherwise next towards 0
120        // the sign is a separate bit in floats, so bits+1 moves away from 0 no matter the float
121        let b = x.to_bits();
122        let bits = if (y > x) == (x > 0.0) { b + 1 } else { b - 1 };
123        let ret = f64::from_bits(bits);
124        if ret == 0.0 { ret.copysign(x) } else { ret }
125    }
126}
127
128#[allow(clippy::float_cmp)]
129pub fn nextafter_with_steps(x: f64, y: f64, steps: u64) -> f64 {
130    if x == y {
131        y
132    } else if x.is_nan() || y.is_nan() {
133        f64::NAN
134    } else if x >= f64::INFINITY {
135        f64::MAX
136    } else if x <= f64::NEG_INFINITY {
137        f64::MIN
138    } else if x == 0.0 {
139        f64::from_bits(1).copysign(y)
140    } else {
141        if steps == 0 {
142            return x;
143        }
144
145        if x.is_nan() {
146            return x;
147        }
148
149        if y.is_nan() {
150            return y;
151        }
152
153        let sign_bit: u64 = 1 << 63;
154
155        let mut ux = x.to_bits();
156        let uy = y.to_bits();
157
158        let ax = ux & !sign_bit;
159        let ay = uy & !sign_bit;
160
161        // If signs are different
162        if ((ux ^ uy) & sign_bit) != 0 {
163            return if ax + ay <= steps {
164                f64::from_bits(uy)
165            } else if ax < steps {
166                let result = (uy & sign_bit) | (steps - ax);
167                f64::from_bits(result)
168            } else {
169                ux -= steps;
170                f64::from_bits(ux)
171            };
172        }
173
174        // If signs are the same
175        if ax > ay {
176            if ax - ay >= steps {
177                ux -= steps;
178                f64::from_bits(ux)
179            } else {
180                f64::from_bits(uy)
181            }
182        } else if ay - ax >= steps {
183            ux += steps;
184            f64::from_bits(ux)
185        } else {
186            f64::from_bits(uy)
187        }
188    }
189}
190
191pub fn ulp(x: f64) -> f64 {
192    if x.is_nan() {
193        return x;
194    }
195    let x = x.abs();
196    let x2 = nextafter(x, f64::INFINITY);
197    if x2.is_infinite() {
198        // special case: x is the largest positive representable float
199        let x2 = nextafter(x, f64::NEG_INFINITY);
200        x - x2
201    } else {
202        x2 - x
203    }
204}
205
206pub fn round_float_digits(x: f64, ndigits: i32) -> Option<f64> {
207    let float = if ndigits.is_zero() {
208        let fract = x.fract();
209        if (fract.abs() - 0.5).abs() < f64::EPSILON {
210            if x.trunc() % 2.0 == 0.0 {
211                x - fract
212            } else {
213                x + fract
214            }
215        } else {
216            x.round()
217        }
218    } else {
219        const NDIGITS_MAX: i32 =
220            ((f64::MANTISSA_DIGITS as i32 - f64::MIN_EXP) as f64 * f64::consts::LOG10_2) as i32;
221        const NDIGITS_MIN: i32 = -(((f64::MAX_EXP + 1) as f64 * f64::consts::LOG10_2) as i32);
222        if ndigits > NDIGITS_MAX {
223            x
224        } else if ndigits < NDIGITS_MIN {
225            0.0f64.copysign(x)
226        } else {
227            let (y, pow1, pow2) = if ndigits >= 0 {
228                // according to cpython: pow1 and pow2 are each safe from overflow, but
229                //                       pow1*pow2 ~= pow(10.0, ndigits) might overflow
230                let (pow1, pow2) = if ndigits > 22 {
231                    (10.0.powf((ndigits - 22) as f64), 1e22)
232                } else {
233                    (10.0.powf(ndigits as f64), 1.0)
234                };
235                let y = (x * pow1) * pow2;
236                if !y.is_finite() {
237                    return Some(x);
238                }
239                (y, pow1, Some(pow2))
240            } else {
241                let pow1 = 10.0.powf((-ndigits) as f64);
242                (x / pow1, pow1, None)
243            };
244            let z = y.round();
245            #[allow(clippy::float_cmp)]
246            let z = if (y - z).abs() == 0.5 {
247                2.0 * (y / 2.0).round()
248            } else {
249                z
250            };
251            let z = if let Some(pow2) = pow2 {
252                // ndigits >= 0
253                (z / pow2) / pow1
254            } else {
255                z * pow1
256            };
257
258            if !z.is_finite() {
259                // overflow
260                return None;
261            }
262
263            z
264        }
265    };
266    Some(float)
267}