|
| 1 | +#![cfg(test)] |
| 2 | + |
| 3 | +use libm::*; |
| 4 | +#[test] |
| 5 | +fn remquo_q_overflow() { |
| 6 | + // 0xc000000000000001, 0x04c0000000000004 |
| 7 | + let _ = remquo(-2.0000000000000004, 8.406091369059082e-286); |
| 8 | +} |
| 9 | + |
| 10 | +#[test] |
| 11 | +fn ceil_sanity_check() { |
| 12 | + assert_eq!(ceil(1.1), 2.0); |
| 13 | + assert_eq!(ceil(2.9), 3.0); |
| 14 | +} |
| 15 | + |
| 16 | +#[test] |
| 17 | +fn atan2_sanity_check() { |
| 18 | + use std::f64::consts::PI; |
| 19 | + assert_eq!(atan2(0.0, 1.0), 0.0); |
| 20 | + assert_eq!(atan2(0.0, -1.0), PI); |
| 21 | + assert_eq!(atan2(-0.0, -1.0), -PI); |
| 22 | + assert_eq!(atan2(3.0, 2.0), atan(3.0 / 2.0)); |
| 23 | + assert_eq!(atan2(2.0, -1.0), atan(2.0 / -1.0) + PI); |
| 24 | + assert_eq!(atan2(-2.0, -1.0), atan(-2.0 / -1.0) - PI); |
| 25 | +} |
| 26 | + |
| 27 | +#[test] |
| 28 | +fn floor_overflow() { |
| 29 | + assert_eq!(floorf(0.5), 0.0); |
| 30 | +} |
| 31 | + |
| 32 | +#[test] |
| 33 | +fn trunc_sanity_check() { |
| 34 | + assert_eq!(trunc(1.1), 1.0); |
| 35 | +} |
| 36 | + |
| 37 | +mod pow { |
| 38 | + use libm::*; |
| 39 | + use std::f64::consts::{E, PI}; |
| 40 | + use std::f64::{EPSILON, INFINITY, MAX, MIN, MIN_POSITIVE, NAN, NEG_INFINITY}; |
| 41 | + |
| 42 | + const POS_ZERO: &[f64] = &[0.0]; |
| 43 | + const NEG_ZERO: &[f64] = &[-0.0]; |
| 44 | + const POS_ONE: &[f64] = &[1.0]; |
| 45 | + const NEG_ONE: &[f64] = &[-1.0]; |
| 46 | + const POS_FLOATS: &[f64] = &[99.0 / 70.0, E, PI]; |
| 47 | + const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -E, -PI]; |
| 48 | + const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), MIN_POSITIVE, EPSILON]; |
| 49 | + const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -MIN_POSITIVE, -EPSILON]; |
| 50 | + const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, MAX]; |
| 51 | + const NEG_EVENS: &[f64] = &[MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0]; |
| 52 | + const POS_ODDS: &[f64] = &[3.0, 7.0]; |
| 53 | + const NEG_ODDS: &[f64] = &[-7.0, -3.0]; |
| 54 | + const NANS: &[f64] = &[NAN]; |
| 55 | + const POS_INF: &[f64] = &[INFINITY]; |
| 56 | + const NEG_INF: &[f64] = &[NEG_INFINITY]; |
| 57 | + |
| 58 | + const ALL: &[&[f64]] = &[ |
| 59 | + POS_ZERO, |
| 60 | + NEG_ZERO, |
| 61 | + NANS, |
| 62 | + NEG_SMALL_FLOATS, |
| 63 | + POS_SMALL_FLOATS, |
| 64 | + NEG_FLOATS, |
| 65 | + POS_FLOATS, |
| 66 | + NEG_EVENS, |
| 67 | + POS_EVENS, |
| 68 | + NEG_ODDS, |
| 69 | + POS_ODDS, |
| 70 | + NEG_INF, |
| 71 | + POS_INF, |
| 72 | + NEG_ONE, |
| 73 | + POS_ONE, |
| 74 | + ]; |
| 75 | + const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF]; |
| 76 | + const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF]; |
| 77 | + |
| 78 | + fn pow_test(base: f64, exponent: f64, expected: f64) { |
| 79 | + let res = pow(base, exponent); |
| 80 | + assert!( |
| 81 | + if expected.is_nan() { |
| 82 | + res.is_nan() |
| 83 | + } else { |
| 84 | + pow(base, exponent) == expected |
| 85 | + }, |
| 86 | + "{} ** {} was {} instead of {}", |
| 87 | + base, |
| 88 | + exponent, |
| 89 | + res, |
| 90 | + expected |
| 91 | + ); |
| 92 | + } |
| 93 | + |
| 94 | + fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) { |
| 95 | + sets.iter() |
| 96 | + .for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected))); |
| 97 | + } |
| 98 | + |
| 99 | + fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) { |
| 100 | + sets.iter() |
| 101 | + .for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected))); |
| 102 | + } |
| 103 | + |
| 104 | + fn test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) { |
| 105 | + sets.iter().for_each(|s| { |
| 106 | + s.iter().for_each(|val| { |
| 107 | + let exp = expected(*val); |
| 108 | + let res = computed(*val); |
| 109 | + |
| 110 | + assert!( |
| 111 | + if exp.is_nan() { |
| 112 | + res.is_nan() |
| 113 | + } else { |
| 114 | + exp == res |
| 115 | + }, |
| 116 | + "test for {} was {} instead of {}", |
| 117 | + val, |
| 118 | + res, |
| 119 | + exp |
| 120 | + ); |
| 121 | + }) |
| 122 | + }); |
| 123 | + } |
| 124 | + |
| 125 | + #[test] |
| 126 | + fn zero_as_exponent() { |
| 127 | + test_sets_as_base(ALL, 0.0, 1.0); |
| 128 | + test_sets_as_base(ALL, -0.0, 1.0); |
| 129 | + } |
| 130 | + |
| 131 | + #[test] |
| 132 | + fn one_as_base() { |
| 133 | + test_sets_as_exponent(1.0, ALL, 1.0); |
| 134 | + } |
| 135 | + |
| 136 | + #[test] |
| 137 | + fn nan_inputs() { |
| 138 | + // NAN as the base: |
| 139 | + // (NAN ^ anything *but 0* should be NAN) |
| 140 | + test_sets_as_exponent(NAN, &ALL[2..], NAN); |
| 141 | + |
| 142 | + // NAN as the exponent: |
| 143 | + // (anything *but 1* ^ NAN should be NAN) |
| 144 | + test_sets_as_base(&ALL[..(ALL.len() - 2)], NAN, NAN); |
| 145 | + } |
| 146 | + |
| 147 | + #[test] |
| 148 | + fn infinity_as_base() { |
| 149 | + // Positive Infinity as the base: |
| 150 | + // (+Infinity ^ positive anything but 0 and NAN should be +Infinity) |
| 151 | + test_sets_as_exponent(INFINITY, &POS[1..], INFINITY); |
| 152 | + |
| 153 | + // (+Infinity ^ negative anything except 0 and NAN should be 0.0) |
| 154 | + test_sets_as_exponent(INFINITY, &NEG[1..], 0.0); |
| 155 | + |
| 156 | + // Negative Infinity as the base: |
| 157 | + // (-Infinity ^ positive odd ints should be -Infinity) |
| 158 | + test_sets_as_exponent(NEG_INFINITY, &[POS_ODDS], NEG_INFINITY); |
| 159 | + |
| 160 | + // (-Infinity ^ anything but odd ints should be == -0 ^ (-anything)) |
| 161 | + // We can lump in pos/neg odd ints here because they don't seem to |
| 162 | + // cause panics (div by zero) in release mode (I think). |
| 163 | + test_sets(ALL, &|v: f64| pow(NEG_INFINITY, v), &|v: f64| pow(-0.0, -v)); |
| 164 | + } |
| 165 | + |
| 166 | + #[test] |
| 167 | + fn infinity_as_exponent() { |
| 168 | + // Positive/Negative base greater than 1: |
| 169 | + // (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base) |
| 170 | + test_sets_as_base(&ALL[5..(ALL.len() - 2)], INFINITY, INFINITY); |
| 171 | + |
| 172 | + // (pos/neg > 1 ^ -Infinity should be 0.0) |
| 173 | + test_sets_as_base(&ALL[5..ALL.len() - 2], NEG_INFINITY, 0.0); |
| 174 | + |
| 175 | + // Positive/Negative base less than 1: |
| 176 | + let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS]; |
| 177 | + |
| 178 | + // (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base) |
| 179 | + test_sets_as_base(base_below_one, INFINITY, 0.0); |
| 180 | + |
| 181 | + // (pos/neg < 1 ^ -Infinity should be Infinity) |
| 182 | + test_sets_as_base(base_below_one, NEG_INFINITY, INFINITY); |
| 183 | + |
| 184 | + // Positive/Negative 1 as the base: |
| 185 | + // (pos/neg 1 ^ Infinity should be 1) |
| 186 | + test_sets_as_base(&[NEG_ONE, POS_ONE], INFINITY, 1.0); |
| 187 | + |
| 188 | + // (pos/neg 1 ^ -Infinity should be 1) |
| 189 | + test_sets_as_base(&[NEG_ONE, POS_ONE], NEG_INFINITY, 1.0); |
| 190 | + } |
| 191 | + |
| 192 | + #[test] |
| 193 | + fn zero_as_base() { |
| 194 | + // Positive Zero as the base: |
| 195 | + // (+0 ^ anything positive but 0 and NAN should be +0) |
| 196 | + test_sets_as_exponent(0.0, &POS[1..], 0.0); |
| 197 | + |
| 198 | + // (+0 ^ anything negative but 0 and NAN should be Infinity) |
| 199 | + // (this should panic because we're dividing by zero) |
| 200 | + test_sets_as_exponent(0.0, &NEG[1..], INFINITY); |
| 201 | + |
| 202 | + // Negative Zero as the base: |
| 203 | + // (-0 ^ anything positive but 0, NAN, and odd ints should be +0) |
| 204 | + test_sets_as_exponent(-0.0, &POS[3..], 0.0); |
| 205 | + |
| 206 | + // (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity) |
| 207 | + // (should panic because of divide by zero) |
| 208 | + test_sets_as_exponent(-0.0, &NEG[3..], INFINITY); |
| 209 | + |
| 210 | + // (-0 ^ positive odd ints should be -0) |
| 211 | + test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0); |
| 212 | + |
| 213 | + // (-0 ^ negative odd ints should be -Infinity) |
| 214 | + // (should panic because of divide by zero) |
| 215 | + test_sets_as_exponent(-0.0, &[NEG_ODDS], NEG_INFINITY); |
| 216 | + } |
| 217 | + |
| 218 | + #[test] |
| 219 | + fn special_cases() { |
| 220 | + // One as the exponent: |
| 221 | + // (anything ^ 1 should be anything - i.e. the base) |
| 222 | + test_sets(ALL, &|v: f64| pow(v, 1.0), &|v: f64| v); |
| 223 | + |
| 224 | + // Negative One as the exponent: |
| 225 | + // (anything ^ -1 should be 1/anything) |
| 226 | + test_sets(ALL, &|v: f64| pow(v, -1.0), &|v: f64| 1.0 / v); |
| 227 | + |
| 228 | + // Factoring -1 out: |
| 229 | + // (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer)) |
| 230 | + &[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS] |
| 231 | + .iter() |
| 232 | + .for_each(|int_set| { |
| 233 | + int_set.iter().for_each(|int| { |
| 234 | + test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| { |
| 235 | + pow(-1.0, *int) * pow(v, *int) |
| 236 | + }); |
| 237 | + }) |
| 238 | + }); |
| 239 | + |
| 240 | + // Negative base (imaginary results): |
| 241 | + // (-anything except 0 and Infinity ^ non-integer should be NAN) |
| 242 | + &NEG[1..(NEG.len() - 1)].iter().for_each(|set| { |
| 243 | + set.iter().for_each(|val| { |
| 244 | + test_sets(&ALL[3..7], &|v: f64| pow(*val, v), &|_| NAN); |
| 245 | + }) |
| 246 | + }); |
| 247 | + } |
| 248 | + |
| 249 | + #[test] |
| 250 | + fn normal_cases() { |
| 251 | + assert_eq!(pow(2.0, 20.0), (1 << 20) as f64); |
| 252 | + assert_eq!(pow(-1.0, 9.0), -1.0); |
| 253 | + assert!(pow(-1.0, 2.2).is_nan()); |
| 254 | + assert!(pow(-1.0, -1.14).is_nan()); |
| 255 | + } |
| 256 | +} |
| 257 | + |
| 258 | +mod atan { |
| 259 | + use libm::atan; |
| 260 | + use std::f64; |
| 261 | + |
| 262 | + #[test] |
| 263 | + fn sanity_check() { |
| 264 | + for (input, answer) in [ |
| 265 | + (3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6), |
| 266 | + (1.0, f64::consts::FRAC_PI_4), |
| 267 | + (3.0_f64.sqrt(), f64::consts::FRAC_PI_3), |
| 268 | + (-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6), |
| 269 | + (-1.0, -f64::consts::FRAC_PI_4), |
| 270 | + (-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3), |
| 271 | + ] |
| 272 | + .iter() |
| 273 | + { |
| 274 | + assert!( |
| 275 | + (atan(*input) - answer) / answer < 1e-5, |
| 276 | + "\natan({:.4}/16) = {:.4}, actual: {}", |
| 277 | + input * 16.0, |
| 278 | + answer, |
| 279 | + atan(*input) |
| 280 | + ); |
| 281 | + } |
| 282 | + } |
| 283 | + |
| 284 | + #[test] |
| 285 | + fn zero() { |
| 286 | + assert_eq!(atan(0.0), 0.0); |
| 287 | + } |
| 288 | + |
| 289 | + #[test] |
| 290 | + fn infinity() { |
| 291 | + assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2); |
| 292 | + } |
| 293 | + |
| 294 | + #[test] |
| 295 | + fn minus_infinity() { |
| 296 | + assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2); |
| 297 | + } |
| 298 | + |
| 299 | + #[test] |
| 300 | + fn nan() { |
| 301 | + assert!(atan(f64::NAN).is_nan()); |
| 302 | + } |
| 303 | +} |
| 304 | + |
| 305 | +#[test] |
| 306 | +fn sin_near_pi() { |
| 307 | + let x = f64::from_bits(0x400921fb000FD5DD); // 3.141592026217707 |
| 308 | + let sx = f64::from_bits(0x3ea50d15ced1a4a2); // 6.273720864039205e-7 |
| 309 | + assert_eq!(sin(x), sx); |
| 310 | +} |
| 311 | + |
| 312 | +#[test] |
| 313 | +fn truncf_sanity_check() { |
| 314 | + assert_eq!(truncf(1.1), 1.0); |
| 315 | +} |
| 316 | + |
| 317 | +#[test] |
| 318 | +fn expm1_sanity_check() { |
| 319 | + assert_eq!(expm1(1.1), 2.0041660239464334); |
| 320 | +} |
| 321 | + |
| 322 | +#[test] |
| 323 | +fn roundf_negative_zero() { |
| 324 | + assert_eq!(roundf(-0.0_f32).to_bits(), (-0.0_f32).to_bits()); |
| 325 | +} |
| 326 | + |
| 327 | +#[test] |
| 328 | +fn exp2_i0_wrap_test() { |
| 329 | + let x = -3.0 / 256.0; |
| 330 | + assert_eq!(exp2(x), f64::from_bits(0x3fefbdba3692d514)); |
| 331 | +} |
| 332 | + |
| 333 | +#[test] |
| 334 | +fn round_negative_zero() { |
| 335 | + assert_eq!(round(-0.0_f64).to_bits(), (-0.0_f64).to_bits()); |
| 336 | +} |
| 337 | + |
| 338 | +#[test] |
| 339 | +fn j1f_2488() { |
| 340 | + // 0x401F3E49 |
| 341 | + assert_eq!(j1f(2.4881766_f32), 0.49999475_f32); |
| 342 | +} |
| 343 | +#[test] |
| 344 | +fn y1f_2002() { |
| 345 | + assert_eq!(y1f(2.0000002_f32), -0.10703229_f32); |
| 346 | +} |
0 commit comments