temper_core/
float64.rs

1use super::{Error, Result};
2use std::sync::Arc;
3
4pub fn cmp(x: f64, y: f64) -> i32 {
5    match (x.is_nan(), y.is_nan()) {
6        (false, false) => match (x == 0.0, y == 0.0) {
7            (true, true) => (x.signum() - y.signum()) as i32,
8            _ => x.partial_cmp(&y).unwrap() as i32,
9        },
10        (x, y) => (x as i32) - (y as i32),
11    }
12    .signum()
13}
14
15pub fn cmp_option(x: Option<f64>, y: Option<f64>) -> i32 {
16    match (x, y) {
17        (Some(x), Some(y)) => cmp(x, y),
18        _ => x.partial_cmp(&y).unwrap() as i32,
19    }
20}
21
22pub fn div(x: f64, y: f64) -> Result<f64> {
23    if y == 0.0 {
24        return Err(Error::new());
25    }
26    Ok(x / y)
27}
28
29pub fn max(x: f64, y: f64) -> f64 {
30    if x.is_nan() || y.is_nan() {
31        return f64::NAN;
32    }
33    x.max(y)
34}
35
36pub fn min(x: f64, y: f64) -> f64 {
37    if x.is_nan() || y.is_nan() {
38        return f64::NAN;
39    }
40    x.min(y)
41}
42
43pub fn near(x: f64, y: f64, rel_tol: Option<f64>, abs_tol: Option<f64>) -> bool {
44    let rel_tol = rel_tol.unwrap_or(1e-9);
45    let abs_tol = abs_tol.unwrap_or(0.0);
46    let margin = (x.abs().max(y.abs()) * rel_tol).max(abs_tol);
47    (x - y).abs() < margin
48}
49
50pub fn rem(x: f64, y: f64) -> Result<f64> {
51    if y == 0.0 {
52        return Err(Error::new());
53    }
54    Ok(x % y)
55}
56
57pub fn sign(x: f64) -> f64 {
58    match () {
59        _ if x == 0.0 => x,
60        _ => x.signum(),
61    }
62}
63
64pub fn to_int(x: f64) -> Result<i32> {
65    match () {
66        _ if x > (i32::MIN as f64) - 1.0 && x < (i32::MAX as f64) + 1.0 => Ok(x as i32),
67        _ => Err(Error::new()),
68    }
69}
70
71const MANTISSA_MAX: f64 = (1i64 << 53) as f64;
72
73pub fn to_int64(x: f64) -> Result<i64> {
74    match () {
75        _ if x > -MANTISSA_MAX && x < MANTISSA_MAX => Ok(x as i64),
76        _ => Err(Error::new()),
77    }
78}
79
80pub fn to_string(x: f64) -> Arc<String> {
81    match () {
82        // No floats in patterns, but still can compare.
83        _ if x == f64::INFINITY => "Infinity".to_string(),
84        _ if x == f64::NEG_INFINITY => "-Infinity".to_string(),
85        // "NaN" is default for NaN, but avoid later logic.
86        _ if x.is_nan() => "NaN".to_string(),
87        _ => match () {
88            _ if x == 0.0 || (0.001..10_000_000.0).contains(&x.abs()) => {
89                let mut text = x.to_string();
90                if !text.contains('.') {
91                    text.push_str(".0");
92                }
93                text
94            }
95            _ => {
96                let mut text = format!("{x:e}");
97                if x.abs() > 1.0 {
98                    let index = text.find('e').unwrap();
99                    let replacement = match () {
100                        _ if text.contains('.') => "e+",
101                        _ => ".0e+",
102                    };
103                    text.replace_range(index..index + 1, replacement);
104                }
105                text
106            }
107        },
108    }
109    .into()
110}