add test infrastructure for f64 functions
This commit is contained in:
@@ -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<Error>> {
|
||||
// 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<Error>> {
|
||||
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<Error>> {
|
||||
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<Error>> {
|
||||
// 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<Error>> {
|
||||
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<Error>> {
|
||||
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<Error>> {
|
||||
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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user