stv_rs/arithmetic/
float64.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Module implementing the [`Integer`] and [`Rational`] traits for [`f64`].
16
17use super::{Integer, IntegerRef, Rational, RationalRef};
18use log::trace;
19use num::traits::Zero;
20
21impl Integer for f64 {
22    fn from_usize(i: usize) -> Self {
23        i as f64
24    }
25
26    #[cfg(test)]
27    fn get_positive_test_values() -> Vec<Self> {
28        let mut result = Vec::new();
29        for i in 0..=30 {
30            result.push((1 << i) as f64);
31            result.push((0x7FFF_FFFF ^ (1 << i)) as f64);
32        }
33        result
34    }
35}
36
37impl IntegerRef<f64> for &f64 {}
38
39impl RationalRef<&f64, f64> for &f64 {}
40
41impl Rational<f64> for f64 {
42    fn from_int(i: f64) -> Self {
43        i
44    }
45
46    fn ratio_i(num: f64, denom: f64) -> Self {
47        num / denom
48    }
49
50    fn to_f64(&self) -> f64 {
51        *self
52    }
53
54    fn assert_eq(a: Self, b: Self, msg: &str) {
55        if a != b {
56            let error = 2f64 * (a - b).abs() / (a.abs() + b.abs());
57            let error_eps = (error / f64::EPSILON).round() as usize;
58            if error_eps <= 1000 {
59                trace!("{msg}: Failed comparison {a} != {b} (error = {error_eps} * eps)");
60            } else {
61                panic!("{msg}: Failed comparison {a} != {b} (error = {error_eps} * eps)");
62            }
63        }
64    }
65
66    fn epsilon() -> Self {
67        Self::zero()
68    }
69
70    fn is_exact() -> bool {
71        true
72    }
73
74    fn description() -> &'static str {
75        "64-bit floating-point arithmetic"
76    }
77
78    fn div_up_as_keep_factor(&self, rhs: &Self) -> Self {
79        self / rhs
80    }
81
82    #[cfg(test)]
83    fn get_positive_test_values() -> Vec<Self> {
84        let mut result = Vec::new();
85        for i in 0..=30 {
86            result.push((1 << i) as f64);
87        }
88        for i in 0..=30 {
89            result.push((0x7FFF_FFFF ^ (1 << i)) as f64);
90        }
91        for i in 0..=30 {
92            result.push(1.0 / (1 << i) as f64);
93        }
94        for i in 0..=30 {
95            result.push(1.0 / (0x7FFF_FFFF ^ (1 << i)) as f64);
96        }
97        result
98    }
99}
100
101#[cfg(test)]
102mod test {
103    use super::*;
104    use crate::util::log_tester::ThreadLocalLogger;
105    use crate::{
106        big_integer_tests, big_numeric_tests, integer_tests, numeric_benchmarks, numeric_tests,
107    };
108    use log::Level::Trace;
109
110    integer_tests!(
111        f64,
112        testi_values_are_positive,
113        testi_is_zero,
114        testi_zero_is_add_neutral,
115        testi_add_is_commutative,
116        testi_opposite,
117        testi_sub_self,
118        testi_add_sub,
119        testi_sub_add,
120        testi_one_is_mul_neutral,
121        testi_mul_is_commutative,
122    );
123
124    big_integer_tests!(
125        f64,
126        None,
127        testi_add_is_associative,
128        testi_mul_is_associative => fail(r"assertion `left == right` failed: (a * b) * c != a * (b * c) for 2147483646, 2147483646, 2147483583
129  left: 9.903519996076708e27
130 right: 9.903519996076707e27"),
131        testi_mul_is_distributive => fail(r"assertion `left == right` failed: a * (b + c) != (a * b) + (a * c) for 2147483646, 1, 2147483519
132  left: 4.6116857392545137e18
133 right: 4.611685739254514e18"),
134        testi_product,
135    );
136
137    numeric_tests!(
138        f64,
139        f64,
140        test_values_are_positive,
141        test_is_exact,
142        test_ratio,
143        test_ratio_invert => fail(r"assertion `left == right` failed: R::ratio(1, a) * a != 1 for 49
144  left: 0.9999999999999999
145 right: 1.0"),
146        test_is_zero,
147        test_zero_is_add_neutral,
148        test_add_is_commutative,
149        test_opposite,
150        test_sub_self,
151        test_add_sub => fail(r"assertion `left == right` failed: (a + b) - b != a for 1, 0.0000000004656613430357376
152  left: 0.9999999999999999
153 right: 1.0"),
154        test_sub_add => fail(r"assertion `left == right` failed: (a - b) + b != a for 0.00000011920928955078125, 2147483646
155  left: 0.0
156 right: 1.1920928955078125e-7"),
157        test_one_is_mul_neutral,
158        test_mul_is_commutative,
159        test_mul_up_is_commutative,
160        test_mul_up_integers,
161        test_mul_up_wrt_mul,
162        test_one_is_div_up_neutral,
163        test_div_up_self,
164        test_mul_div_up => fail(r"assertion `left == right` failed: div_up(a * b, b) != a for 2147483646, 0.000000000465661315280157
165  left: 2147483646.0000002
166 right: 2147483646.0"),
167        test_mul_by_int,
168        test_mul_div_by_int => fail(r"assertion `left == right` failed: a * int(b) / int(b) != a for 0.0000000005321843286349222, 7
169  left: 5.321843286349223e-10
170 right: 5.321843286349222e-10"),
171        test_references,
172        test_assign,
173    );
174
175    big_numeric_tests!(
176        f64,
177        f64,
178        None,
179        test_add_is_associative => fail(r"assertion `left == right` failed: (a + b) + c != a + (b + c) for 1, 1, 0.0000000004656615095692907
180  left: 2.0000000004656617
181 right: 2.0000000004656613"),
182        test_mul_is_associative => fail(r"assertion `left == right` failed: (a * b) * c != a * (b * c) for 2147483646, 2147483646, 2147483583
183  left: 9.903519996076708e27
184 right: 9.903519996076707e27"),
185        test_mul_is_distributive => fail(r"assertion `left == right` failed: a * (b + c) != (a * b) + (a * c) for 2147483646, 1, 2147483519
186  left: 4.6116857392545137e18
187 right: 4.611685739254514e18"),
188        test_mul_by_int_is_associative => fail(r"assertion `left == right` failed: (a * b) * c != a * (b * c) for 0.0000000004656612944634737, 3, 3
189  left: 4.190951650171264e-9
190 right: 4.190951650171263e-9"),
191        test_mul_by_int_is_distributive => fail(r"assertion `left == right` failed: (a + b) * c != (a * c) + (b * c) for 1, 0.0000000004656613985469087, 3
192  left: 3.0000000013969848
193 right: 3.0000000013969843"),
194        test_div_by_int_is_associative => fail(r"assertion `left == right` failed: (a / b) / c != a / (b * c) for 1, 3, 11
195  left: 0.0303030303030303
196 right: 0.030303030303030304"),
197        test_div_by_int_is_distributive => fail(r"assertion `left == right` failed: (a + b) / c != (a / c) + (b / c) for 1, 2, 5
198  left: 0.6
199 right: 0.6000000000000001"),
200        test_sum,
201        test_product,
202    );
203
204    numeric_benchmarks!(f64, f64, bench_add, bench_sub, bench_mul, bench_div_up,);
205
206    #[test]
207    fn test_description() {
208        assert_eq!(f64::description(), "64-bit floating-point arithmetic");
209    }
210
211    #[test]
212    fn test_assert_eq() {
213        let logger = ThreadLocalLogger::start();
214        f64::assert_eq(0.0, 0.0, "Error message");
215        logger.check_target_logs("stv_rs::arithmetic::float64", []);
216
217        let logger = ThreadLocalLogger::start();
218        f64::assert_eq(1.0, 1.0 + f64::EPSILON, "Error message");
219        logger.check_target_logs(
220            "stv_rs::arithmetic::float64",
221            [(
222                Trace,
223                "Error message: Failed comparison 1 != 1.0000000000000002 (error = 1 * eps)",
224            )],
225        );
226
227        let logger = ThreadLocalLogger::start();
228        f64::assert_eq(1.0, 1.0 + 999.0 * f64::EPSILON, "Error message");
229        logger.check_target_logs(
230            "stv_rs::arithmetic::float64",
231            [(
232                Trace,
233                "Error message: Failed comparison 1 != 1.0000000000002218 (error = 999 * eps)",
234            )],
235        );
236
237        let logger = ThreadLocalLogger::start();
238        f64::assert_eq(1.0, 1.0 + 1000.0 * f64::EPSILON, "Error message");
239        logger.check_target_logs(
240            "stv_rs::arithmetic::float64",
241            [(
242                Trace,
243                "Error message: Failed comparison 1 != 1.000000000000222 (error = 1000 * eps)",
244            )],
245        );
246    }
247
248    #[test]
249    #[should_panic(
250        expected = "Error message: Failed comparison 1 != 1.0000000000002223 (error = 1001 * eps)"
251    )]
252    fn test_assert_ne() {
253        f64::assert_eq(1.0, 1.0 + 1001.0 * f64::EPSILON, "Error message");
254    }
255
256    #[test]
257    fn test_display_test_values() {
258        #[rustfmt::skip]
259        let expected_displays = [
260          "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192",
261          "16384", "32768", "65536", "131072", "262144", "524288", "1048576", "2097152", "4194304",
262          "8388608", "16777216", "33554432", "67108864", "134217728", "268435456", "536870912",
263          "1073741824", "2147483646", "2147483645", "2147483643", "2147483639", "2147483631",
264          "2147483615", "2147483583", "2147483519", "2147483391", "2147483135", "2147482623",
265          "2147481599", "2147479551", "2147475455", "2147467263", "2147450879", "2147418111",
266          "2147352575", "2147221503", "2146959359", "2146435071", "2145386495", "2143289343",
267          "2139095039", "2130706431", "2113929215", "2080374783", "2013265919", "1879048191",
268          "1610612735", "1073741823",
269          "1", "0.5", "0.25", "0.125", "0.0625", "0.03125", "0.015625", "0.0078125", "0.00390625",
270          "0.001953125", "0.0009765625", "0.00048828125", "0.000244140625", "0.0001220703125",
271          "0.00006103515625", "0.000030517578125", "0.0000152587890625", "0.00000762939453125",
272          "0.000003814697265625", "0.0000019073486328125", "0.00000095367431640625",
273          "0.000000476837158203125", "0.0000002384185791015625", "0.00000011920928955078125",
274          "0.00000005960464477539063", "0.000000029802322387695313", "0.000000014901161193847656",
275          "0.000000007450580596923828", "0.000000003725290298461914", "0.000000001862645149230957",
276          "0.0000000009313225746154785", "0.0000000004656612877414201",
277          "0.0000000004656612879582606", "0.0000000004656612883919414",
278          "0.0000000004656612892593032", "0.0000000004656612909940266",
279          "0.0000000004656612944634737", "0.0000000004656613014023679",
280          "0.000000000465661315280157", "0.0000000004656613430357376",
281          "0.0000000004656613985469087", "0.0000000004656615095692907",
282          "0.0000000004656617316142135", "0.0000000004656621757046943",
283          "0.000000000465663063888197", "0.0000000004656648402653671",
284          "0.0000000004656683930603658", "0.0000000004656754988130022",
285          "0.0000000004656897109688659", "0.0000000004657181378832345",
286          "0.0000000004657750021247607", "0.0000000004658887722767739",
287          "0.0000000004661164793992049", "0.000000000466572562060278",
288          "0.0000000004674874102216082", "0.0000000004693279118375177",
289          "0.0000000004730527365364029", "0.0000000004806826193874318",
290          "0.0000000004967053733749714", "0.0000000005321843286349222",
291          "0.0000000006208817167958132", "0.0000000009313225754828403"
292        ];
293        let actual_displays: Vec<String> = <f64 as Rational<_>>::get_positive_test_values()
294            .iter()
295            .map(|x| format!("{x}"))
296            .collect();
297        assert_eq!(actual_displays, expected_displays);
298    }
299}