Skip to main content

std_cmp/
std_cmp.rs

1use std::f64::consts::PI;
2
3const STEP: f64 = 0.000001;
4
5/// Run a sweep of precision comparisons against std
6/// Precision will be different platform to platform
7/// On my computer, I get:
8/// ```ignore
9/// Func   | Total Tests| Diff Count|       Max Diff
10/// acos   |     2000000|     349419|    4.44089e-16
11/// acosh  |    99000001|    8505318|    8.88178e-16
12/// asin   |     2000000|     173790|    2.22045e-16
13/// asinh  |    99000001|    8714913|    8.88178e-16
14/// atan   |    50265483|    3290826|    2.22045e-16
15/// atanh  |     1999998|     771064|    3.34115e-11
16/// cos    |    50265483|    2173339|    1.11022e-16
17/// cosh   |    25132742|    6498663|    2.91038e-11
18/// ln     |    99999001|    3413955|    8.88178e-16
19/// exp    |    20000001|    1944323|    3.63798e-12
20/// fabs   |    20000001|          0|      0.00000e0
21/// floor  |    20000001|          0|      0.00000e0
22/// sin    |    50265483|    2255609|    1.11022e-16
23/// sinh   |    25132742|    7200641|    2.91038e-11
24/// sqrt   |    10000001|    2500953|    4.44089e-16
25/// tan    |    50265483|   20777207|     3.72529e-9
26/// ```
27fn main() {
28    println!(
29        "{:<7}|{:>12}|{:>11}|{:>15}",
30        "Func", "Total Tests", "Diff Count", "Max Diff"
31    );
32    let tests = [
33        CompareArgs {
34            name: "acos".to_string(),
35            start: -1.0,
36            stop: 1.0,
37            step: STEP,
38            std_fn: |x| x.acos(),
39            const_fn: |x: f64| trig_const::acos(x),
40        },
41        CompareArgs {
42            name: "acosh".to_string(),
43            start: 1.0,
44            stop: 100.0,
45            step: STEP,
46            std_fn: |x| x.acosh(),
47            const_fn: |x: f64| trig_const::acosh(x),
48        },
49        CompareArgs {
50            name: "asin".to_string(),
51            start: -1.0,
52            stop: 1.0,
53            step: STEP,
54            std_fn: |x| x.asin(),
55            const_fn: |x: f64| trig_const::asin(x),
56        },
57        CompareArgs {
58            name: "asinh".to_string(),
59            start: 1.0,
60            stop: 100.0,
61            step: STEP,
62            std_fn: |x| x.asinh(),
63            const_fn: |x: f64| trig_const::asinh(x),
64        },
65        CompareArgs {
66            name: "atan".to_string(),
67            start: -8.0 * PI,
68            stop: 8.0 * PI,
69            step: STEP,
70            std_fn: |x| x.atan(),
71            const_fn: |x: f64| trig_const::atan(x),
72        },
73        CompareArgs {
74            name: "atanh".to_string(),
75            start: -1.0 + STEP,
76            stop: 1.0 - STEP,
77            step: STEP,
78            std_fn: |x| x.atanh(),
79            const_fn: |x| trig_const::atanh(x),
80        },
81        CompareArgs {
82            name: "cos".to_string(),
83            start: -8.0 * PI,
84            stop: 8.0 * PI,
85            step: STEP,
86            std_fn: |x| x.cos(),
87            const_fn: |x: f64| trig_const::cos(x),
88        },
89        CompareArgs {
90            name: "cosh".to_string(),
91            start: -4.0 * PI,
92            stop: 4.0 * PI,
93            step: STEP,
94            std_fn: |x| x.cosh(),
95            const_fn: |x| trig_const::cosh(x),
96        },
97        CompareArgs {
98            name: "ln".to_string(),
99            start: 0.001,
100            stop: 100.0,
101            step: STEP,
102            std_fn: |x| x.ln(),
103            const_fn: |x: f64| trig_const::ln(x),
104        },
105        CompareArgs {
106            name: "exp".to_string(),
107            start: -10.0,
108            stop: 10.0,
109            step: STEP,
110            std_fn: |x| x.exp(),
111            const_fn: |x: f64| trig_const::exp(x),
112        },
113        CompareArgs {
114            name: "fabs".to_string(),
115            start: -10.0,
116            stop: 10.0,
117            step: STEP,
118            std_fn: |x| x.abs(),
119            const_fn: |x: f64| trig_const::fabs(x),
120        },
121        CompareArgs {
122            name: "floor".to_string(),
123            start: -10.0,
124            stop: 10.0,
125            step: STEP,
126            std_fn: |x| x.floor(),
127            const_fn: |x: f64| trig_const::floor(x),
128        },
129        CompareArgs {
130            name: "sin".to_string(),
131            start: -8.0 * PI,
132            stop: 8.0 * PI,
133            step: STEP,
134            std_fn: |x| x.sin(),
135            const_fn: |x: f64| trig_const::sin(x),
136        },
137        CompareArgs {
138            name: "sinh".to_string(),
139            start: -4.0 * PI,
140            stop: 4.0 * PI,
141            step: STEP,
142            std_fn: |x| x.sinh(),
143            const_fn: |x| trig_const::sinh(x),
144        },
145        CompareArgs {
146            name: "sqrt".to_string(),
147            start: 0.0,
148            stop: 10.0,
149            step: STEP,
150            std_fn: |x| x.sqrt(),
151            const_fn: |x: f64| trig_const::sqrt(x),
152        },
153        CompareArgs {
154            name: "tan".to_string(),
155            start: -8.0 * PI,
156            stop: 8.0 * PI,
157            step: STEP,
158            std_fn: |x| x.tan(),
159            const_fn: |x: f64| trig_const::tan(x),
160        },
161    ];
162
163    for test in tests {
164        let diff = compare_functions(&test);
165        println!(
166            "{:<7}|{:>12}|{:>11}|{:>15.5e}",
167            test.name, diff.total_tests, diff.diff_tests, diff.max_diff
168        );
169    }
170}
171
172fn compare_functions(c: &CompareArgs) -> DiffCounter {
173    let mut const_metric = DiffCounter::default();
174
175    for x in float_loop(c.start, c.stop, c.step) {
176        let real = (c.std_fn)(x);
177        let const_result = (c.const_fn)(x);
178
179        const_metric.add_metric(real, const_result);
180    }
181
182    const_metric
183}
184
185#[derive(Debug, Default)]
186struct DiffCounter {
187    total_tests: usize,
188    diff_tests: usize,
189    max_diff: f64,
190}
191
192impl DiffCounter {
193    fn add_metric(&mut self, real: f64, actual: f64) {
194        self.total_tests += 1;
195        let diff = (real - actual).abs();
196        if diff != 0.0 {
197            self.diff_tests += 1;
198            self.max_diff = f64::max(self.max_diff, diff);
199        }
200    }
201}
202
203fn float_loop(start: f64, stop: f64, step: f64) -> impl Iterator<Item = f64> {
204    core::iter::successors(Some(start), move |prev| {
205        let next = prev + step;
206        (next < stop).then_some(next)
207    })
208}
209
210struct CompareArgs {
211    name: String,
212    start: f64,
213    stop: f64,
214    step: f64,
215    std_fn: fn(f64) -> f64,
216    const_fn: fn(f64) -> f64,
217}