rustpython_common/
float_ops.rs1use 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
16pub 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 (Some(_), None) => other_int.is_positive(),
46 _ if value.is_infinite() => value.is_sign_negative(),
48 _ => 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 (Some(_), None) => other_int.is_negative(),
59 _ if value.is_infinite() => value.is_sign_positive(),
61 _ => 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#[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 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 ((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 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 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 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 (z / pow2) / pow1
254 } else {
255 z * pow1
256 };
257
258 if !z.is_finite() {
259 return None;
261 }
262
263 z
264 }
265 };
266 Some(float)
267}