From fdfc13afb9c52dca7fa67d93a5206b112d87f0d0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 12 Jul 2018 14:24:02 -0500 Subject: [PATCH] add test infrastructure for f64 functions --- library/compiler-builtins/libm/README.md | 1 + library/compiler-builtins/libm/src/fabs.rs | 5 + library/compiler-builtins/libm/src/lib.rs | 7 + .../libm/test-generator/src/main.rs | 228 +++++++++++++++++- 4 files changed, 230 insertions(+), 11 deletions(-) create mode 100644 library/compiler-builtins/libm/src/fabs.rs diff --git a/library/compiler-builtins/libm/README.md b/library/compiler-builtins/libm/README.md index 17c4ee94df56..a0b131733d20 100644 --- a/library/compiler-builtins/libm/README.md +++ b/library/compiler-builtins/libm/README.md @@ -96,6 +96,7 @@ cf. [rustwasm/team#84](https://github.com/rustwasm/team/issues/84). ### Other functions +- [x] fabs - [x] fabsf - [x] scalbnf - [x] sqrtf diff --git a/library/compiler-builtins/libm/src/fabs.rs b/library/compiler-builtins/libm/src/fabs.rs new file mode 100644 index 000000000000..993918efcfbf --- /dev/null +++ b/library/compiler-builtins/libm/src/fabs.rs @@ -0,0 +1,5 @@ +use core::u64; + +pub fn fabs(x: f64) -> f64 { + f64::from_bits(x.to_bits() & (u64::MAX / 2)) +} diff --git a/library/compiler-builtins/libm/src/lib.rs b/library/compiler-builtins/libm/src/lib.rs index 41c20b2dae5a..3f71fe876c6f 100644 --- a/library/compiler-builtins/libm/src/lib.rs +++ b/library/compiler-builtins/libm/src/lib.rs @@ -1,12 +1,14 @@ #![deny(warnings)] #![no_std] +mod fabs; mod fabsf; mod fmodf; mod powf; mod scalbnf; mod sqrtf; +pub use fabs::fabs; pub use fabsf::fabsf; pub use fmodf::fmodf; pub use powf::powf; @@ -19,6 +21,11 @@ pub fn _eqf(a: u32, b: u32) -> bool { (a as i32).wrapping_sub(b as i32).abs() <= 1 } +#[doc(hidden)] +pub fn _eq(a: u64, b: u64) -> bool { + (a as i64).wrapping_sub(b as i64).abs() <= 1 +} + fn isnanf(x: f32) -> bool { x.to_bits() & 0x7fffffff > 0x7f800000 } diff --git a/library/compiler-builtins/libm/test-generator/src/main.rs b/library/compiler-builtins/libm/test-generator/src/main.rs index 3e58f7e1a081..fb9154aecc69 100644 --- a/library/compiler-builtins/libm/test-generator/src/main.rs +++ b/library/compiler-builtins/libm/test-generator/src/main.rs @@ -10,14 +10,14 @@ use std::error::Error; use std::fmt::Write as _0; use std::fs::{self, File}; use std::io::Write as _1; -use std::{i16, u32, u8}; +use std::{i16, u16, u32, u64, u8}; use rand::{Rng, SeedableRng, XorShiftRng}; // Number of test cases to generate const NTESTS: usize = 10_000; -// TODO tweak this function to generate edge cases (zero, infinity, NaN) more often +// TODO tweak these functions to generate edge cases (zero, infinity, NaN) more often fn f32(rng: &mut XorShiftRng) -> f32 { let sign = if rng.gen_bool(0.5) { 1 << 31 } else { 0 }; let exponent = (rng.gen_range(0, u8::MAX) as u32) << 23; @@ -26,13 +26,21 @@ fn f32(rng: &mut XorShiftRng) -> f32 { f32::from_bits(sign + exponent + mantissa) } +fn f64(rng: &mut XorShiftRng) -> f64 { + let sign = if rng.gen_bool(0.5) { 1 << 63 } else { 0 }; + let exponent = (rng.gen_range(0, u16::MAX) as u64 & ((1 << 11) - 1)) << 52; + let mantissa = rng.gen_range(0, u64::MAX) & ((1 << 52) - 1); + + f64::from_bits(sign + exponent + mantissa) +} + // fn(f32) -> f32 macro_rules! f32_f32 { - ($($intr:ident,)+) => { + ($($intr:ident,)*) => { fn f32_f32(rng: &mut XorShiftRng) -> Result<(), Box> { // MUSL C implementation of the function to test extern "C" { - $(fn $intr(_: f32) -> f32;)+ + $(fn $intr(_: f32) -> f32;)* } $( @@ -78,18 +86,19 @@ macro_rules! f32_f32 { ", stringify!($intr), cases)?; - )+ + )* Ok(()) } } } +// fn(f32, f32) -> f32 macro_rules! f32f32_f32 { - ($($intr:ident,)+) => { + ($($intr:ident,)*) => { fn f32f32_f32(rng: &mut XorShiftRng) -> Result<(), Box> { extern "C" { - $(fn $intr(_: f32, _: f32) -> f32;)+ + $(fn $intr(_: f32, _: f32) -> f32;)* } $( @@ -137,18 +146,19 @@ macro_rules! f32f32_f32 { ", stringify!($intr), cases)?; - )+ + )* Ok(()) } }; } +// fn(f32, i32) -> f32 macro_rules! f32i32_f32 { - ($($intr:ident,)+) => { + ($($intr:ident,)*) => { fn f32i32_f32(rng: &mut XorShiftRng) -> Result<(), Box> { extern "C" { - $(fn $intr(_: f32, _: i32) -> f32;)+ + $(fn $intr(_: f32, _: i32) -> f32;)* } $( @@ -195,7 +205,185 @@ macro_rules! f32i32_f32 { ", stringify!($intr), cases)?; - )+ + )* + + Ok(()) + } + }; +} + +// fn(f64) -> f64 +macro_rules! f64_f64 { + ($($intr:ident,)*) => { + fn f64_f64(rng: &mut XorShiftRng) -> Result<(), Box> { + // MUSL C implementation of the function to test + extern "C" { + $(fn $intr(_: f64) -> f64;)* + } + + $( + let mut cases = String::new(); + for _ in 0..NTESTS { + let inp = f64(rng); + let out = unsafe { $intr(inp) }; + + let inp = inp.to_bits(); + let out = out.to_bits(); + + write!(cases, "({}, {})", inp, out).unwrap(); + cases.push(','); + } + + let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?; + write!(f, " + extern crate libm; + + #[test] + fn {0}() {{ + const CASES: &[(u64, u64)] = &[ + {1} + ]; + + for case in CASES {{ + let (inp, expected) = *case; + + let outf = libm::{0}(f64::from_bits(inp)); + let outi = outf.to_bits(); + + if !((outf.is_nan() && f64::from_bits(expected).is_nan()) || + libm::_eq(outi, expected)) {{ + panic!( + \"input: {{}}, output: {{}}, expected: {{}}\", + inp, + outi, + expected, + ); + }} + }} + }} +", + stringify!($intr), + cases)?; + )* + + Ok(()) + } + } +} + +// fn(f64, f64) -> f64 +macro_rules! f64f64_f64 { + ($($intr:ident,)*) => { + fn f64f64_f64(rng: &mut XorShiftRng) -> Result<(), Box> { + extern "C" { + $(fn $intr(_: f64, _: f64) -> f64;)* + } + + $( + let mut cases = String::new(); + for _ in 0..NTESTS { + let i1 = f64(rng); + let i2 = f64(rng); + let out = unsafe { $intr(i1, i2) }; + + let i1 = i1.to_bits(); + let i2 = i2.to_bits(); + let out = out.to_bits(); + + write!(cases, "(({}, {}), {})", i1, i2, out).unwrap(); + cases.push(','); + } + + let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?; + write!(f, " + extern crate libm; + + #[test] + fn {0}() {{ + const CASES: &[((u64, u64), u64)] = &[ + {1} + ]; + + for case in CASES {{ + let ((i1, i2), expected) = *case; + + let outf = libm::{0}(f64::from_bits(i1), f64::from_bits(i2)); + let outi = outf.to_bits(); + + if !((outf.is_nan() && f64::from_bits(expected).is_nan()) || + libm::_eq(outi, expected)) {{ + panic!( + \"input: {{:?}}, output: {{}}, expected: {{}}\", + (i1, i2), + outi, + expected, + ); + }} + }} + }} +", + stringify!($intr), + cases)?; + )* + + Ok(()) + } + }; +} + +// fn(f64, i32) -> f64 +macro_rules! f64i32_f64 { + ($($intr:ident,)*) => { + fn f64i32_f64(rng: &mut XorShiftRng) -> Result<(), Box> { + extern "C" { + $(fn $intr(_: f64, _: i32) -> f64;)* + } + + $( + let mut cases = String::new(); + for _ in 0..NTESTS { + let i1 = f64(rng); + let i2 = rng.gen_range(i16::MIN, i16::MAX); + let out = unsafe { $intr(i1, i2 as i32) }; + + let i1 = i1.to_bits(); + let out = out.to_bits(); + + write!(cases, "(({}, {}), {})", i1, i2, out).unwrap(); + cases.push(','); + } + + let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?; + write!(f, " + extern crate libm; + + #[test] + fn {0}() {{ + const CASES: &[((u64, i16), u64)] = &[ + {1} + ]; + + for case in CASES {{ + let ((i1, i2), expected) = *case; + + let outf = libm::{0}(f64::from_bits(i1), i2 as i32); + let outi = outf.to_bits(); + + if !((outf.is_nan() && f64::from_bits(expected).is_nan()) || + libm::_eq(outi, expected)) {{ + panic!( + \"input: {{:?}}, output: {{}}, expected: {{}}\", + (i1, i2), + outi, + expected, + ); + }} + }} + }} +", + stringify!($intr), + cases)?; + )* Ok(()) } @@ -211,6 +399,9 @@ fn main() -> Result<(), Box> { f32_f32(&mut rng)?; f32f32_f32(&mut rng)?; f32i32_f32(&mut rng)?; + f64_f64(&mut rng)?; + f64f64_f64(&mut rng)?; + f64i32_f64(&mut rng)?; Ok(()) } @@ -233,3 +424,18 @@ f32f32_f32! { f32i32_f32! { scalbnf, } + +// With signature `fn(f64) -> f64` +f64_f64! { + fabs, +} + +// With signature `fn(f64, f64) -> f64` +f64f64_f64! { + // fmod, +} + +// With signature `fn(f64, i32) -> f64` +f64i32_f64! { + // scalbn, +}