• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![cfg(all(not(feature = "std"), feature = "compact"))]
2 
3 // These are adapted from libm, a port of musl libc's libm to Rust.
4 // libm can be found online [here](https://github.com/rust-lang/libm),
5 // and is similarly licensed under an Apache2.0/MIT license
6 
7 use core::f64;
8 use minimal_lexical::libm;
9 
10 #[test]
fabsf_sanity_test()11 fn fabsf_sanity_test() {
12     assert_eq!(libm::fabsf(-1.0), 1.0);
13     assert_eq!(libm::fabsf(2.8), 2.8);
14 }
15 
16 /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs
17 #[test]
fabsf_spec_test()18 fn fabsf_spec_test() {
19     assert!(libm::fabsf(f32::NAN).is_nan());
20     for f in [0.0, -0.0].iter().copied() {
21         assert_eq!(libm::fabsf(f), 0.0);
22     }
23     for f in [f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
24         assert_eq!(libm::fabsf(f), f32::INFINITY);
25     }
26 }
27 
28 #[test]
sqrtf_sanity_test()29 fn sqrtf_sanity_test() {
30     assert_eq!(libm::sqrtf(100.0), 10.0);
31     assert_eq!(libm::sqrtf(4.0), 2.0);
32 }
33 
34 /// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt
35 #[test]
sqrtf_spec_test()36 fn sqrtf_spec_test() {
37     // Not Asserted: FE_INVALID exception is raised if argument is negative.
38     assert!(libm::sqrtf(-1.0).is_nan());
39     assert!(libm::sqrtf(f32::NAN).is_nan());
40     for f in [0.0, -0.0, f32::INFINITY].iter().copied() {
41         assert_eq!(libm::sqrtf(f), f);
42     }
43 }
44 
45 const POS_ZERO: &[f64] = &[0.0];
46 const NEG_ZERO: &[f64] = &[-0.0];
47 const POS_ONE: &[f64] = &[1.0];
48 const NEG_ONE: &[f64] = &[-1.0];
49 const POS_FLOATS: &[f64] = &[99.0 / 70.0, f64::consts::E, f64::consts::PI];
50 const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -f64::consts::E, -f64::consts::PI];
51 const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), f64::MIN_POSITIVE, f64::EPSILON];
52 const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -f64::MIN_POSITIVE, -f64::EPSILON];
53 const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, f64::MAX];
54 const NEG_EVENS: &[f64] = &[f64::MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0];
55 const POS_ODDS: &[f64] = &[3.0, 7.0];
56 const NEG_ODDS: &[f64] = &[-7.0, -3.0];
57 const NANS: &[f64] = &[f64::NAN];
58 const POS_INF: &[f64] = &[f64::INFINITY];
59 const NEG_INF: &[f64] = &[f64::NEG_INFINITY];
60 
61 const ALL: &[&[f64]] = &[
62     POS_ZERO,
63     NEG_ZERO,
64     NANS,
65     NEG_SMALL_FLOATS,
66     POS_SMALL_FLOATS,
67     NEG_FLOATS,
68     POS_FLOATS,
69     NEG_EVENS,
70     POS_EVENS,
71     NEG_ODDS,
72     POS_ODDS,
73     NEG_INF,
74     POS_INF,
75     NEG_ONE,
76     POS_ONE,
77 ];
78 const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF];
79 const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF];
80 
powd(base: f64, exponent: f64, expected: f64)81 fn powd(base: f64, exponent: f64, expected: f64) {
82     let res = libm::powd(base, exponent);
83     assert!(
84         if expected.is_nan() {
85             res.is_nan()
86         } else {
87             libm::powd(base, exponent) == expected
88         },
89         "{} ** {} was {} instead of {}",
90         base,
91         exponent,
92         res,
93         expected
94     );
95 }
96 
powd_test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64)97 fn powd_test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) {
98     sets.iter().for_each(|s| s.iter().for_each(|val| powd(*val, exponent, expected)));
99 }
100 
powd_test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64)101 fn powd_test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) {
102     sets.iter().for_each(|s| s.iter().for_each(|val| powd(base, *val, expected)));
103 }
104 
powd_test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64)105 fn powd_test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) {
106     sets.iter().for_each(|s| {
107         s.iter().for_each(|val| {
108             let exp = expected(*val);
109             let res = computed(*val);
110 
111             assert!(
112                 if exp.is_nan() {
113                     res.is_nan()
114                 } else {
115                     exp == res
116                 },
117                 "test for {} was {} instead of {}",
118                 val,
119                 res,
120                 exp
121             );
122         })
123     });
124 }
125 
126 #[test]
powd_zero_as_exponent()127 fn powd_zero_as_exponent() {
128     powd_test_sets_as_base(ALL, 0.0, 1.0);
129     powd_test_sets_as_base(ALL, -0.0, 1.0);
130 }
131 
132 #[test]
powd_one_as_base()133 fn powd_one_as_base() {
134     powd_test_sets_as_exponent(1.0, ALL, 1.0);
135 }
136 
137 #[test]
powd_nan_inputs()138 fn powd_nan_inputs() {
139     // NAN as the base:
140     // (NAN ^ anything *but 0* should be NAN)
141     powd_test_sets_as_exponent(f64::NAN, &ALL[2..], f64::NAN);
142 
143     // NAN as the exponent:
144     // (anything *but 1* ^ NAN should be NAN)
145     powd_test_sets_as_base(&ALL[..(ALL.len() - 2)], f64::NAN, f64::NAN);
146 }
147 
148 #[test]
powd_infinity_as_base()149 fn powd_infinity_as_base() {
150     // Positive Infinity as the base:
151     // (+Infinity ^ positive anything but 0 and NAN should be +Infinity)
152     powd_test_sets_as_exponent(f64::INFINITY, &POS[1..], f64::INFINITY);
153 
154     // (+Infinity ^ negative anything except 0 and NAN should be 0.0)
155     powd_test_sets_as_exponent(f64::INFINITY, &NEG[1..], 0.0);
156 
157     // Negative Infinity as the base:
158     // (-Infinity ^ positive odd ints should be -Infinity)
159     powd_test_sets_as_exponent(f64::NEG_INFINITY, &[POS_ODDS], f64::NEG_INFINITY);
160 
161     // (-Infinity ^ anything but odd ints should be == -0 ^ (-anything))
162     // We can lump in pos/neg odd ints here because they don't seem to
163     // cause panics (div by zero) in release mode (I think).
164     powd_test_sets(ALL, &|v: f64| libm::powd(f64::NEG_INFINITY, v), &|v: f64| libm::powd(-0.0, -v));
165 }
166 
167 #[test]
infinity_as_exponent()168 fn infinity_as_exponent() {
169     // Positive/Negative base greater than 1:
170     // (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base)
171     powd_test_sets_as_base(&ALL[5..(ALL.len() - 2)], f64::INFINITY, f64::INFINITY);
172 
173     // (pos/neg > 1 ^ -Infinity should be 0.0)
174     powd_test_sets_as_base(&ALL[5..ALL.len() - 2], f64::NEG_INFINITY, 0.0);
175 
176     // Positive/Negative base less than 1:
177     let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS];
178 
179     // (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base)
180     powd_test_sets_as_base(base_below_one, f64::INFINITY, 0.0);
181 
182     // (pos/neg < 1 ^ -Infinity should be Infinity)
183     powd_test_sets_as_base(base_below_one, f64::NEG_INFINITY, f64::INFINITY);
184 
185     // Positive/Negative 1 as the base:
186     // (pos/neg 1 ^ Infinity should be 1)
187     powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::INFINITY, 1.0);
188 
189     // (pos/neg 1 ^ -Infinity should be 1)
190     powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::NEG_INFINITY, 1.0);
191 }
192 
193 #[test]
powd_zero_as_base()194 fn powd_zero_as_base() {
195     // Positive Zero as the base:
196     // (+0 ^ anything positive but 0 and NAN should be +0)
197     powd_test_sets_as_exponent(0.0, &POS[1..], 0.0);
198 
199     // (+0 ^ anything negative but 0 and NAN should be Infinity)
200     // (this should panic because we're dividing by zero)
201     powd_test_sets_as_exponent(0.0, &NEG[1..], f64::INFINITY);
202 
203     // Negative Zero as the base:
204     // (-0 ^ anything positive but 0, NAN, and odd ints should be +0)
205     powd_test_sets_as_exponent(-0.0, &POS[3..], 0.0);
206 
207     // (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity)
208     // (should panic because of divide by zero)
209     powd_test_sets_as_exponent(-0.0, &NEG[3..], f64::INFINITY);
210 
211     // (-0 ^ positive odd ints should be -0)
212     powd_test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0);
213 
214     // (-0 ^ negative odd ints should be -Infinity)
215     // (should panic because of divide by zero)
216     powd_test_sets_as_exponent(-0.0, &[NEG_ODDS], f64::NEG_INFINITY);
217 }
218 
219 #[test]
special_cases()220 fn special_cases() {
221     // One as the exponent:
222     // (anything ^ 1 should be anything - i.e. the base)
223     powd_test_sets(ALL, &|v: f64| libm::powd(v, 1.0), &|v: f64| v);
224 
225     // Negative One as the exponent:
226     // (anything ^ -1 should be 1/anything)
227     powd_test_sets(ALL, &|v: f64| libm::powd(v, -1.0), &|v: f64| 1.0 / v);
228 
229     // Factoring -1 out:
230     // (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer))
231     [POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS].iter().for_each(|int_set| {
232         int_set.iter().for_each(|int| {
233             powd_test_sets(ALL, &|v: f64| libm::powd(-v, *int), &|v: f64| {
234                 libm::powd(-1.0, *int) * libm::powd(v, *int)
235             });
236         })
237     });
238 
239     // Negative base (imaginary results):
240     // (-anything except 0 and Infinity ^ non-integer should be NAN)
241     NEG[1..(NEG.len() - 1)].iter().for_each(|set| {
242         set.iter().for_each(|val| {
243             powd_test_sets(&ALL[3..7], &|v: f64| libm::powd(*val, v), &|_| f64::NAN);
244         })
245     });
246 }
247 
248 #[test]
normal_cases()249 fn normal_cases() {
250     assert_eq!(libm::powd(2.0, 20.0), (1 << 20) as f64);
251     assert_eq!(libm::powd(-1.0, 9.0), -1.0);
252     assert!(libm::powd(-1.0, 2.2).is_nan());
253     assert!(libm::powd(-1.0, -1.14).is_nan());
254 }
255 
256 #[test]
fabsd_sanity_test()257 fn fabsd_sanity_test() {
258     assert_eq!(libm::fabsd(-1.0), 1.0);
259     assert_eq!(libm::fabsd(2.8), 2.8);
260 }
261 
262 /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs
263 #[test]
fabsd_spec_test()264 fn fabsd_spec_test() {
265     assert!(libm::fabsd(f64::NAN).is_nan());
266     for f in [0.0, -0.0].iter().copied() {
267         assert_eq!(libm::fabsd(f), 0.0);
268     }
269     for f in [f64::INFINITY, f64::NEG_INFINITY].iter().copied() {
270         assert_eq!(libm::fabsd(f), f64::INFINITY);
271     }
272 }
273 
274 #[test]
sqrtd_sanity_test()275 fn sqrtd_sanity_test() {
276     assert_eq!(libm::sqrtd(100.0), 10.0);
277     assert_eq!(libm::sqrtd(4.0), 2.0);
278 }
279 
280 /// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt
281 #[test]
sqrtd_spec_test()282 fn sqrtd_spec_test() {
283     // Not Asserted: FE_INVALID exception is raised if argument is negative.
284     assert!(libm::sqrtd(-1.0).is_nan());
285     assert!(libm::sqrtd(f64::NAN).is_nan());
286     for f in [0.0, -0.0, f64::INFINITY].iter().copied() {
287         assert_eq!(libm::sqrtd(f), f);
288     }
289 }
290