From 0613a31a5b1468fad46e212d96164c34c9c51b04 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Tue, 9 Jul 2019 17:24:39 +0200 Subject: [PATCH 1/3] Move unit tests into its own file --- src/math/atan.rs | 48 ------- src/math/atan2.rs | 10 -- src/math/ceil.rs | 9 -- src/math/exp2.rs | 8 +- src/math/expm1.rs | 8 -- src/math/floorf.rs | 8 -- src/math/j1f.rs | 14 -- src/math/pow.rs | 224 ----------------------------- src/math/remquo.rs | 11 -- src/math/round.rs | 10 -- src/math/trunc.rs | 8 -- src/math/truncf.rs | 8 -- tests/unit.rs | 347 +++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 348 insertions(+), 365 deletions(-) create mode 100644 tests/unit.rs diff --git a/src/math/atan.rs b/src/math/atan.rs index d2684ece8..f79fc0eb0 100644 --- a/src/math/atan.rs +++ b/src/math/atan.rs @@ -135,51 +135,3 @@ pub fn atan(x: f64) -> f64 { z } } - -#[cfg(test)] -mod tests { - use super::atan; - use core::f64; - - #[test] - fn sanity_check() { - for (input, answer) in [ - (3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6), - (1.0, f64::consts::FRAC_PI_4), - (3.0_f64.sqrt(), f64::consts::FRAC_PI_3), - (-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6), - (-1.0, -f64::consts::FRAC_PI_4), - (-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3), - ] - .iter() - { - assert!( - (atan(*input) - answer) / answer < 1e-5, - "\natan({:.4}/16) = {:.4}, actual: {}", - input * 16.0, - answer, - atan(*input) - ); - } - } - - #[test] - fn zero() { - assert_eq!(atan(0.0), 0.0); - } - - #[test] - fn infinity() { - assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2); - } - - #[test] - fn minus_infinity() { - assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2); - } - - #[test] - fn nan() { - assert!(atan(f64::NAN).is_nan()); - } -} diff --git a/src/math/atan2.rs b/src/math/atan2.rs index 08385cd10..a303dcc60 100644 --- a/src/math/atan2.rs +++ b/src/math/atan2.rs @@ -115,13 +115,3 @@ pub fn atan2(y: f64, x: f64) -> f64 { _ => (z - PI_LO) - PI, /* atan(-,-) */ } } - -#[test] -fn sanity_check() { - assert_eq!(atan2(0.0, 1.0), 0.0); - assert_eq!(atan2(0.0, -1.0), PI); - assert_eq!(atan2(-0.0, -1.0), -PI); - assert_eq!(atan2(3.0, 2.0), atan(3.0 / 2.0)); - assert_eq!(atan2(2.0, -1.0), atan(2.0 / -1.0) + PI); - assert_eq!(atan2(-2.0, -1.0), atan(-2.0 / -1.0) - PI); -} diff --git a/src/math/ceil.rs b/src/math/ceil.rs index 59883a8a7..bb10eea53 100644 --- a/src/math/ceil.rs +++ b/src/math/ceil.rs @@ -40,12 +40,3 @@ pub fn ceil(x: f64) -> f64 { x + y } } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::ceil(1.1), 2.0); - assert_eq!(super::ceil(2.9), 3.0); - } -} diff --git a/src/math/exp2.rs b/src/math/exp2.rs index c2192fde5..b9b534b73 100644 --- a/src/math/exp2.rs +++ b/src/math/exp2.rs @@ -28,7 +28,7 @@ use super::scalbn; const TBLSIZE: usize = 256; -#[cfg_attr(rustfmt, rustfmt_skip)] +#[rustfmt::skip] static TBL: [u64; TBLSIZE * 2] = [ // exp2(z + eps) eps 0x3fe6a09e667f3d5d, 0x3d39880000000000, @@ -387,9 +387,3 @@ pub fn exp2(mut x: f64) -> f64 { scalbn(r, ki) } - -#[test] -fn i0_wrap_test() { - let x = -3.0 / 256.0; - assert_eq!(exp2(x), f64::from_bits(0x3fefbdba3692d514)); -} diff --git a/src/math/expm1.rs b/src/math/expm1.rs index 0d43b4e10..a5f5e4338 100644 --- a/src/math/expm1.rs +++ b/src/math/expm1.rs @@ -135,11 +135,3 @@ pub fn expm1(mut x: f64) -> f64 { } y } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::expm1(1.1), 2.0041660239464334); - } -} diff --git a/src/math/floorf.rs b/src/math/floorf.rs index c04f18aee..ca85e210f 100644 --- a/src/math/floorf.rs +++ b/src/math/floorf.rs @@ -40,11 +40,3 @@ pub fn floorf(x: f32) -> f32 { } f32::from_bits(ui) } - -#[cfg(test)] -mod tests { - #[test] - fn no_overflow() { - assert_eq!(super::floorf(0.5), 0.0); - } -} diff --git a/src/math/j1f.rs b/src/math/j1f.rs index 5095894d7..e0b692a7d 100644 --- a/src/math/j1f.rs +++ b/src/math/j1f.rs @@ -356,17 +356,3 @@ fn qonef(x: f32) -> f32 { s = 1.0 + z * (q[0] + z * (q[1] + z * (q[2] + z * (q[3] + z * (q[4] + z * q[5]))))); return (0.375 + r / s) / x; } - -#[cfg(test)] -mod tests { - use super::{j1f, y1f}; - #[test] - fn test_j1f_2488() { - // 0x401F3E49 - assert_eq!(j1f(2.4881766_f32), 0.49999475_f32); - } - #[test] - fn test_y1f_2002() { - assert_eq!(y1f(2.0000002_f32), -0.10703229_f32); - } -} diff --git a/src/math/pow.rs b/src/math/pow.rs index 068a4ec47..dca70f99b 100644 --- a/src/math/pow.rs +++ b/src/math/pow.rs @@ -408,227 +408,3 @@ pub fn pow(x: f64, y: f64) -> f64 { s * z } - -#[cfg(test)] -mod tests { - extern crate core; - - use self::core::f64::consts::{E, PI}; - use self::core::f64::{EPSILON, INFINITY, MAX, MIN, MIN_POSITIVE, NAN, NEG_INFINITY}; - use super::pow; - - const POS_ZERO: &[f64] = &[0.0]; - const NEG_ZERO: &[f64] = &[-0.0]; - const POS_ONE: &[f64] = &[1.0]; - const NEG_ONE: &[f64] = &[-1.0]; - const POS_FLOATS: &[f64] = &[99.0 / 70.0, E, PI]; - const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -E, -PI]; - const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), MIN_POSITIVE, EPSILON]; - const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -MIN_POSITIVE, -EPSILON]; - const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, MAX]; - const NEG_EVENS: &[f64] = &[MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0]; - const POS_ODDS: &[f64] = &[3.0, 7.0]; - const NEG_ODDS: &[f64] = &[-7.0, -3.0]; - const NANS: &[f64] = &[NAN]; - const POS_INF: &[f64] = &[INFINITY]; - const NEG_INF: &[f64] = &[NEG_INFINITY]; - - const ALL: &[&[f64]] = &[ - POS_ZERO, - NEG_ZERO, - NANS, - NEG_SMALL_FLOATS, - POS_SMALL_FLOATS, - NEG_FLOATS, - POS_FLOATS, - NEG_EVENS, - POS_EVENS, - NEG_ODDS, - POS_ODDS, - NEG_INF, - POS_INF, - NEG_ONE, - POS_ONE, - ]; - const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF]; - const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF]; - - fn pow_test(base: f64, exponent: f64, expected: f64) { - let res = pow(base, exponent); - assert!( - if expected.is_nan() { - res.is_nan() - } else { - pow(base, exponent) == expected - }, - "{} ** {} was {} instead of {}", - base, - exponent, - res, - expected - ); - } - - fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) { - sets.iter() - .for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected))); - } - - fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) { - sets.iter() - .for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected))); - } - - fn test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) { - sets.iter().for_each(|s| { - s.iter().for_each(|val| { - let exp = expected(*val); - let res = computed(*val); - - assert!( - if exp.is_nan() { - res.is_nan() - } else { - exp == res - }, - "test for {} was {} instead of {}", - val, - res, - exp - ); - }) - }); - } - - #[test] - fn zero_as_exponent() { - test_sets_as_base(ALL, 0.0, 1.0); - test_sets_as_base(ALL, -0.0, 1.0); - } - - #[test] - fn one_as_base() { - test_sets_as_exponent(1.0, ALL, 1.0); - } - - #[test] - fn nan_inputs() { - // NAN as the base: - // (NAN ^ anything *but 0* should be NAN) - test_sets_as_exponent(NAN, &ALL[2..], NAN); - - // NAN as the exponent: - // (anything *but 1* ^ NAN should be NAN) - test_sets_as_base(&ALL[..(ALL.len() - 2)], NAN, NAN); - } - - #[test] - fn infinity_as_base() { - // Positive Infinity as the base: - // (+Infinity ^ positive anything but 0 and NAN should be +Infinity) - test_sets_as_exponent(INFINITY, &POS[1..], INFINITY); - - // (+Infinity ^ negative anything except 0 and NAN should be 0.0) - test_sets_as_exponent(INFINITY, &NEG[1..], 0.0); - - // Negative Infinity as the base: - // (-Infinity ^ positive odd ints should be -Infinity) - test_sets_as_exponent(NEG_INFINITY, &[POS_ODDS], NEG_INFINITY); - - // (-Infinity ^ anything but odd ints should be == -0 ^ (-anything)) - // We can lump in pos/neg odd ints here because they don't seem to - // cause panics (div by zero) in release mode (I think). - test_sets(ALL, &|v: f64| pow(NEG_INFINITY, v), &|v: f64| pow(-0.0, -v)); - } - - #[test] - fn infinity_as_exponent() { - // Positive/Negative base greater than 1: - // (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base) - test_sets_as_base(&ALL[5..(ALL.len() - 2)], INFINITY, INFINITY); - - // (pos/neg > 1 ^ -Infinity should be 0.0) - test_sets_as_base(&ALL[5..ALL.len() - 2], NEG_INFINITY, 0.0); - - // Positive/Negative base less than 1: - let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS]; - - // (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base) - test_sets_as_base(base_below_one, INFINITY, 0.0); - - // (pos/neg < 1 ^ -Infinity should be Infinity) - test_sets_as_base(base_below_one, NEG_INFINITY, INFINITY); - - // Positive/Negative 1 as the base: - // (pos/neg 1 ^ Infinity should be 1) - test_sets_as_base(&[NEG_ONE, POS_ONE], INFINITY, 1.0); - - // (pos/neg 1 ^ -Infinity should be 1) - test_sets_as_base(&[NEG_ONE, POS_ONE], NEG_INFINITY, 1.0); - } - - #[test] - fn zero_as_base() { - // Positive Zero as the base: - // (+0 ^ anything positive but 0 and NAN should be +0) - test_sets_as_exponent(0.0, &POS[1..], 0.0); - - // (+0 ^ anything negative but 0 and NAN should be Infinity) - // (this should panic because we're dividing by zero) - test_sets_as_exponent(0.0, &NEG[1..], INFINITY); - - // Negative Zero as the base: - // (-0 ^ anything positive but 0, NAN, and odd ints should be +0) - test_sets_as_exponent(-0.0, &POS[3..], 0.0); - - // (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity) - // (should panic because of divide by zero) - test_sets_as_exponent(-0.0, &NEG[3..], INFINITY); - - // (-0 ^ positive odd ints should be -0) - test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0); - - // (-0 ^ negative odd ints should be -Infinity) - // (should panic because of divide by zero) - test_sets_as_exponent(-0.0, &[NEG_ODDS], NEG_INFINITY); - } - - #[test] - fn special_cases() { - // One as the exponent: - // (anything ^ 1 should be anything - i.e. the base) - test_sets(ALL, &|v: f64| pow(v, 1.0), &|v: f64| v); - - // Negative One as the exponent: - // (anything ^ -1 should be 1/anything) - test_sets(ALL, &|v: f64| pow(v, -1.0), &|v: f64| 1.0 / v); - - // Factoring -1 out: - // (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer)) - &[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS] - .iter() - .for_each(|int_set| { - int_set.iter().for_each(|int| { - test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| { - pow(-1.0, *int) * pow(v, *int) - }); - }) - }); - - // Negative base (imaginary results): - // (-anything except 0 and Infinity ^ non-integer should be NAN) - &NEG[1..(NEG.len() - 1)].iter().for_each(|set| { - set.iter().for_each(|val| { - test_sets(&ALL[3..7], &|v: f64| pow(*val, v), &|_| NAN); - }) - }); - } - - #[test] - fn normal_cases() { - assert_eq!(pow(2.0, 20.0), (1 << 20) as f64); - assert_eq!(pow(-1.0, 9.0), -1.0); - assert!(pow(-1.0, 2.2).is_nan()); - assert!(pow(-1.0, -1.14).is_nan()); - } -} diff --git a/src/math/remquo.rs b/src/math/remquo.rs index c72c8f187..8e027f242 100644 --- a/src/math/remquo.rs +++ b/src/math/remquo.rs @@ -96,14 +96,3 @@ pub fn remquo(mut x: f64, mut y: f64) -> (f64, i32) { (x, quo) } } - -#[cfg(test)] -mod tests { - use super::remquo; - - #[test] - fn test_q_overflow() { - // 0xc000000000000001, 0x04c0000000000004 - let _ = remquo(-2.0000000000000004, 8.406091369059082e-286); - } -} diff --git a/src/math/round.rs b/src/math/round.rs index 67590d2c1..13049fb25 100644 --- a/src/math/round.rs +++ b/src/math/round.rs @@ -35,13 +35,3 @@ pub fn round(mut x: f64) -> f64 { y } } - -#[cfg(test)] -mod tests { - use super::round; - - #[test] - fn negative_zero() { - assert_eq!(round(-0.0_f64).to_bits(), (-0.0_f64).to_bits()); - } -} diff --git a/src/math/trunc.rs b/src/math/trunc.rs index 1ee46fc7d..0b882daa7 100644 --- a/src/math/trunc.rs +++ b/src/math/trunc.rs @@ -31,11 +31,3 @@ pub fn trunc(x: f64) -> f64 { i &= !m; f64::from_bits(i) } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::trunc(1.1), 1.0); - } -} diff --git a/src/math/truncf.rs b/src/math/truncf.rs index f93383269..b366f08dd 100644 --- a/src/math/truncf.rs +++ b/src/math/truncf.rs @@ -31,11 +31,3 @@ pub fn truncf(x: f32) -> f32 { i &= !m; f32::from_bits(i) } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::truncf(1.1), 1.0); - } -} diff --git a/tests/unit.rs b/tests/unit.rs new file mode 100644 index 000000000..8f7a39d57 --- /dev/null +++ b/tests/unit.rs @@ -0,0 +1,347 @@ +#![cfg(test)] + +use libm::*; + +#[test] +fn remquo_q_overflow() { + // 0xc000000000000001, 0x04c0000000000004 + let _ = remquo(-2.0000000000000004, 8.406091369059082e-286); +} + +#[test] +fn ceil_sanity_check() { + assert_eq!(ceil(1.1), 2.0); + assert_eq!(ceil(2.9), 3.0); +} + +#[test] +fn atan2_sanity_check() { + use std::f64::consts::PI; + assert_eq!(atan2(0.0, 1.0), 0.0); + assert_eq!(atan2(0.0, -1.0), PI); + assert_eq!(atan2(-0.0, -1.0), -PI); + assert_eq!(atan2(3.0, 2.0), atan(3.0 / 2.0)); + assert_eq!(atan2(2.0, -1.0), atan(2.0 / -1.0) + PI); + assert_eq!(atan2(-2.0, -1.0), atan(-2.0 / -1.0) - PI); +} + +#[test] +fn floor_overflow() { + assert_eq!(floorf(0.5), 0.0); +} + +#[test] +fn trunc_sanity_check() { + assert_eq!(trunc(1.1), 1.0); +} + +mod pow { + use libm::*; + use std::f64::consts::{E, PI}; + use std::f64::{EPSILON, INFINITY, MAX, MIN, MIN_POSITIVE, NAN, NEG_INFINITY}; + + const POS_ZERO: &[f64] = &[0.0]; + const NEG_ZERO: &[f64] = &[-0.0]; + const POS_ONE: &[f64] = &[1.0]; + const NEG_ONE: &[f64] = &[-1.0]; + const POS_FLOATS: &[f64] = &[99.0 / 70.0, E, PI]; + const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -E, -PI]; + const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), MIN_POSITIVE, EPSILON]; + const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -MIN_POSITIVE, -EPSILON]; + const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, MAX]; + const NEG_EVENS: &[f64] = &[MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0]; + const POS_ODDS: &[f64] = &[3.0, 7.0]; + const NEG_ODDS: &[f64] = &[-7.0, -3.0]; + const NANS: &[f64] = &[NAN]; + const POS_INF: &[f64] = &[INFINITY]; + const NEG_INF: &[f64] = &[NEG_INFINITY]; + + const ALL: &[&[f64]] = &[ + POS_ZERO, + NEG_ZERO, + NANS, + NEG_SMALL_FLOATS, + POS_SMALL_FLOATS, + NEG_FLOATS, + POS_FLOATS, + NEG_EVENS, + POS_EVENS, + NEG_ODDS, + POS_ODDS, + NEG_INF, + POS_INF, + NEG_ONE, + POS_ONE, + ]; + const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF]; + const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF]; + + fn pow_test(base: f64, exponent: f64, expected: f64) { + let res = pow(base, exponent); + assert!( + if expected.is_nan() { + res.is_nan() + } else { + pow(base, exponent) == expected + }, + "{} ** {} was {} instead of {}", + base, + exponent, + res, + expected + ); + } + + fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) { + sets.iter() + .for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected))); + } + + fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) { + sets.iter() + .for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected))); + } + + fn test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) { + sets.iter().for_each(|s| { + s.iter().for_each(|val| { + let exp = expected(*val); + let res = computed(*val); + + assert!( + if exp.is_nan() { + res.is_nan() + } else { + exp == res + }, + "test for {} was {} instead of {}", + val, + res, + exp + ); + }) + }); + } + + #[test] + fn zero_as_exponent() { + test_sets_as_base(ALL, 0.0, 1.0); + test_sets_as_base(ALL, -0.0, 1.0); + } + + #[test] + fn one_as_base() { + test_sets_as_exponent(1.0, ALL, 1.0); + } + + #[test] + fn nan_inputs() { + // NAN as the base: + // (NAN ^ anything *but 0* should be NAN) + test_sets_as_exponent(NAN, &ALL[2..], NAN); + + // NAN as the exponent: + // (anything *but 1* ^ NAN should be NAN) + test_sets_as_base(&ALL[..(ALL.len() - 2)], NAN, NAN); + } + + #[test] + fn infinity_as_base() { + // Positive Infinity as the base: + // (+Infinity ^ positive anything but 0 and NAN should be +Infinity) + test_sets_as_exponent(INFINITY, &POS[1..], INFINITY); + + // (+Infinity ^ negative anything except 0 and NAN should be 0.0) + test_sets_as_exponent(INFINITY, &NEG[1..], 0.0); + + // Negative Infinity as the base: + // (-Infinity ^ positive odd ints should be -Infinity) + test_sets_as_exponent(NEG_INFINITY, &[POS_ODDS], NEG_INFINITY); + + // (-Infinity ^ anything but odd ints should be == -0 ^ (-anything)) + // We can lump in pos/neg odd ints here because they don't seem to + // cause panics (div by zero) in release mode (I think). + test_sets(ALL, &|v: f64| pow(NEG_INFINITY, v), &|v: f64| pow(-0.0, -v)); + } + + #[test] + fn infinity_as_exponent() { + // Positive/Negative base greater than 1: + // (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base) + test_sets_as_base(&ALL[5..(ALL.len() - 2)], INFINITY, INFINITY); + + // (pos/neg > 1 ^ -Infinity should be 0.0) + test_sets_as_base(&ALL[5..ALL.len() - 2], NEG_INFINITY, 0.0); + + // Positive/Negative base less than 1: + let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS]; + + // (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base) + test_sets_as_base(base_below_one, INFINITY, 0.0); + + // (pos/neg < 1 ^ -Infinity should be Infinity) + test_sets_as_base(base_below_one, NEG_INFINITY, INFINITY); + + // Positive/Negative 1 as the base: + // (pos/neg 1 ^ Infinity should be 1) + test_sets_as_base(&[NEG_ONE, POS_ONE], INFINITY, 1.0); + + // (pos/neg 1 ^ -Infinity should be 1) + test_sets_as_base(&[NEG_ONE, POS_ONE], NEG_INFINITY, 1.0); + } + + #[test] + fn zero_as_base() { + // Positive Zero as the base: + // (+0 ^ anything positive but 0 and NAN should be +0) + test_sets_as_exponent(0.0, &POS[1..], 0.0); + + // (+0 ^ anything negative but 0 and NAN should be Infinity) + // (this should panic because we're dividing by zero) + test_sets_as_exponent(0.0, &NEG[1..], INFINITY); + + // Negative Zero as the base: + // (-0 ^ anything positive but 0, NAN, and odd ints should be +0) + test_sets_as_exponent(-0.0, &POS[3..], 0.0); + + // (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity) + // (should panic because of divide by zero) + test_sets_as_exponent(-0.0, &NEG[3..], INFINITY); + + // (-0 ^ positive odd ints should be -0) + test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0); + + // (-0 ^ negative odd ints should be -Infinity) + // (should panic because of divide by zero) + test_sets_as_exponent(-0.0, &[NEG_ODDS], NEG_INFINITY); + } + + #[test] + fn special_cases() { + // One as the exponent: + // (anything ^ 1 should be anything - i.e. the base) + test_sets(ALL, &|v: f64| pow(v, 1.0), &|v: f64| v); + + // Negative One as the exponent: + // (anything ^ -1 should be 1/anything) + test_sets(ALL, &|v: f64| pow(v, -1.0), &|v: f64| 1.0 / v); + + // Factoring -1 out: + // (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer)) + &[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS] + .iter() + .for_each(|int_set| { + int_set.iter().for_each(|int| { + test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| { + pow(-1.0, *int) * pow(v, *int) + }); + }) + }); + + // Negative base (imaginary results): + // (-anything except 0 and Infinity ^ non-integer should be NAN) + &NEG[1..(NEG.len() - 1)].iter().for_each(|set| { + set.iter().for_each(|val| { + test_sets(&ALL[3..7], &|v: f64| pow(*val, v), &|_| NAN); + }) + }); + } + + #[test] + fn normal_cases() { + assert_eq!(pow(2.0, 20.0), (1 << 20) as f64); + assert_eq!(pow(-1.0, 9.0), -1.0); + assert!(pow(-1.0, 2.2).is_nan()); + assert!(pow(-1.0, -1.14).is_nan()); + } +} + +mod atan { + use libm::atan; + use std::f64; + + #[test] + fn sanity_check() { + for (input, answer) in [ + (3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6), + (1.0, f64::consts::FRAC_PI_4), + (3.0_f64.sqrt(), f64::consts::FRAC_PI_3), + (-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6), + (-1.0, -f64::consts::FRAC_PI_4), + (-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3), + ] + .iter() + { + assert!( + (atan(*input) - answer) / answer < 1e-5, + "\natan({:.4}/16) = {:.4}, actual: {}", + input * 16.0, + answer, + atan(*input) + ); + } + } + + #[test] + fn zero() { + assert_eq!(atan(0.0), 0.0); + } + + #[test] + fn infinity() { + assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2); + } + + #[test] + fn minus_infinity() { + assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2); + } + + #[test] + fn nan() { + assert!(atan(f64::NAN).is_nan()); + } +} + +#[test] +fn sin_near_pi() { + let x = f64::from_bits(0x400921fb000FD5DD); // 3.141592026217707 + let sx = f64::from_bits(0x3ea50d15ced1a4a2); // 6.273720864039205e-7 + assert_eq!(sin(x), sx); +} + +#[test] +fn truncf_sanity_check() { + assert_eq!(truncf(1.1), 1.0); +} + +#[test] +fn expm1_sanity_check() { + assert_eq!(expm1(1.1), 2.0041660239464334); +} + +#[test] +fn roundf_negative_zero() { + assert_eq!(roundf(-0.0_f32).to_bits(), (-0.0_f32).to_bits()); +} + +#[test] +fn exp2_i0_wrap_test() { + let x = -3.0 / 256.0; + assert_eq!(exp2(x), f64::from_bits(0x3fefbdba3692d514)); +} + +#[test] +fn round_negative_zero() { + assert_eq!(round(-0.0_f64).to_bits(), (-0.0_f64).to_bits()); +} + +#[test] +fn j1f_2488() { + // 0x401F3E49 + assert_eq!(j1f(2.4881766_f32), 0.49999475_f32); +} +#[test] +fn y1f_2002() { + assert_eq!(y1f(2.0000002_f32), -0.10703229_f32); +} From 7118b00156f1d9e334f04722370f1b1862fdfc6f Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 10 Jul 2019 10:24:25 +0200 Subject: [PATCH 2/3] Move testing functions and musl-tests out --- src/lib.rs | 39 --------------------------------------- tests/unit.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b15857dbe..527fcb4f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,43 +7,4 @@ )] mod math; - -use core::{f32, f64}; - pub use self::math::*; - -/// Approximate equality with 1 ULP of tolerance -#[doc(hidden)] -#[inline] -pub fn _eqf(a: f32, b: f32) -> Result<(), u32> { - if a.is_nan() && b.is_nan() { - Ok(()) - } else { - let err = (a.to_bits() as i32).wrapping_sub(b.to_bits() as i32).abs(); - - if err <= 1 { - Ok(()) - } else { - Err(err as u32) - } - } -} - -#[doc(hidden)] -#[inline] -pub fn _eq(a: f64, b: f64) -> Result<(), u64> { - if a.is_nan() && b.is_nan() { - Ok(()) - } else { - let err = (a.to_bits() as i64).wrapping_sub(b.to_bits() as i64).abs(); - - if err <= 1 { - Ok(()) - } else { - Err(err as u64) - } - } -} - -#[cfg(all(test, feature = "musl-reference-tests"))] -include!(concat!(env!("OUT_DIR"), "/musl-tests.rs")); diff --git a/tests/unit.rs b/tests/unit.rs index 8f7a39d57..8453d22f1 100644 --- a/tests/unit.rs +++ b/tests/unit.rs @@ -1,7 +1,42 @@ #![cfg(test)] +use core::{f32, f64}; use libm::*; +/// Approximate equality with 1 ULP of tolerance +#[inline] +fn _eqf(a: f32, b: f32) -> Result<(), u32> { + if a.is_nan() && b.is_nan() { + Ok(()) + } else { + let err = (a.to_bits() as i32).wrapping_sub(b.to_bits() as i32).abs(); + + if err <= 1 { + Ok(()) + } else { + Err(err as u32) + } + } +} + +#[inline] +fn _eq(a: f64, b: f64) -> Result<(), u64> { + if a.is_nan() && b.is_nan() { + Ok(()) + } else { + let err = (a.to_bits() as i64).wrapping_sub(b.to_bits() as i64).abs(); + + if err <= 1 { + Ok(()) + } else { + Err(err as u64) + } + } +} + +#[cfg(all(test, feature = "musl-reference-tests"))] +include!(concat!(env!("OUT_DIR"), "/musl-tests.rs")); + #[test] fn remquo_q_overflow() { // 0xc000000000000001, 0x04c0000000000004 From 2682865bf2b11d55e73ced885f2bfcec8a7b30ee Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 10 Jul 2019 10:40:35 +0200 Subject: [PATCH 3/3] Move musl reference tests into its own file --- .gitignore | 1 - tests/musl-reference-tests.rs | 37 +++++++++++++++++++++++++++++++++++ tests/unit.rs | 37 ----------------------------------- 3 files changed, 37 insertions(+), 38 deletions(-) create mode 100644 tests/musl-reference-tests.rs diff --git a/.gitignore b/.gitignore index 39950911a..4a6738a35 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ /math/src /math/target /target -/tests Cargo.lock diff --git a/tests/musl-reference-tests.rs b/tests/musl-reference-tests.rs new file mode 100644 index 000000000..be7bb8284 --- /dev/null +++ b/tests/musl-reference-tests.rs @@ -0,0 +1,37 @@ +#![cfg(all(test, feature = "musl-reference-tests"))] + +use core::{f32, f64}; +use libm::*; + +/// Approximate equality with 1 ULP of tolerance +#[inline] +fn _eqf(a: f32, b: f32) -> Result<(), u32> { + if a.is_nan() && b.is_nan() { + Ok(()) + } else { + let err = (a.to_bits() as i32).wrapping_sub(b.to_bits() as i32).abs(); + + if err <= 1 { + Ok(()) + } else { + Err(err as u32) + } + } +} + +#[inline] +fn _eq(a: f64, b: f64) -> Result<(), u64> { + if a.is_nan() && b.is_nan() { + Ok(()) + } else { + let err = (a.to_bits() as i64).wrapping_sub(b.to_bits() as i64).abs(); + + if err <= 1 { + Ok(()) + } else { + Err(err as u64) + } + } +} + +include!(concat!(env!("OUT_DIR"), "/musl-tests.rs")); diff --git a/tests/unit.rs b/tests/unit.rs index 8453d22f1..7e74b1030 100644 --- a/tests/unit.rs +++ b/tests/unit.rs @@ -1,42 +1,5 @@ -#![cfg(test)] - -use core::{f32, f64}; use libm::*; -/// Approximate equality with 1 ULP of tolerance -#[inline] -fn _eqf(a: f32, b: f32) -> Result<(), u32> { - if a.is_nan() && b.is_nan() { - Ok(()) - } else { - let err = (a.to_bits() as i32).wrapping_sub(b.to_bits() as i32).abs(); - - if err <= 1 { - Ok(()) - } else { - Err(err as u32) - } - } -} - -#[inline] -fn _eq(a: f64, b: f64) -> Result<(), u64> { - if a.is_nan() && b.is_nan() { - Ok(()) - } else { - let err = (a.to_bits() as i64).wrapping_sub(b.to_bits() as i64).abs(); - - if err <= 1 { - Ok(()) - } else { - Err(err as u64) - } - } -} - -#[cfg(all(test, feature = "musl-reference-tests"))] -include!(concat!(env!("OUT_DIR"), "/musl-tests.rs")); - #[test] fn remquo_q_overflow() { // 0xc000000000000001, 0x04c0000000000004