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