| 204 | } |
| 205 | |
| 206 | pub 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 |