add test infrastructure for f64 functions
This commit is contained in:
@@ -96,6 +96,7 @@ cf. [rustwasm/team#84](https://github.com/rustwasm/team/issues/84).
|
|||||||
|
|
||||||
### Other functions
|
### Other functions
|
||||||
|
|
||||||
|
- [x] fabs
|
||||||
- [x] fabsf
|
- [x] fabsf
|
||||||
- [x] scalbnf
|
- [x] scalbnf
|
||||||
- [x] sqrtf
|
- [x] sqrtf
|
||||||
|
|||||||
5
library/compiler-builtins/libm/src/fabs.rs
Normal file
5
library/compiler-builtins/libm/src/fabs.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use core::u64;
|
||||||
|
|
||||||
|
pub fn fabs(x: f64) -> f64 {
|
||||||
|
f64::from_bits(x.to_bits() & (u64::MAX / 2))
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
mod fabs;
|
||||||
mod fabsf;
|
mod fabsf;
|
||||||
mod fmodf;
|
mod fmodf;
|
||||||
mod powf;
|
mod powf;
|
||||||
mod scalbnf;
|
mod scalbnf;
|
||||||
mod sqrtf;
|
mod sqrtf;
|
||||||
|
|
||||||
|
pub use fabs::fabs;
|
||||||
pub use fabsf::fabsf;
|
pub use fabsf::fabsf;
|
||||||
pub use fmodf::fmodf;
|
pub use fmodf::fmodf;
|
||||||
pub use powf::powf;
|
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
|
(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 {
|
fn isnanf(x: f32) -> bool {
|
||||||
x.to_bits() & 0x7fffffff > 0x7f800000
|
x.to_bits() & 0x7fffffff > 0x7f800000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ use std::error::Error;
|
|||||||
use std::fmt::Write as _0;
|
use std::fmt::Write as _0;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::Write as _1;
|
use std::io::Write as _1;
|
||||||
use std::{i16, u32, u8};
|
use std::{i16, u16, u32, u64, u8};
|
||||||
|
|
||||||
use rand::{Rng, SeedableRng, XorShiftRng};
|
use rand::{Rng, SeedableRng, XorShiftRng};
|
||||||
|
|
||||||
// Number of test cases to generate
|
// Number of test cases to generate
|
||||||
const NTESTS: usize = 10_000;
|
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 {
|
fn f32(rng: &mut XorShiftRng) -> f32 {
|
||||||
let sign = if rng.gen_bool(0.5) { 1 << 31 } else { 0 };
|
let sign = if rng.gen_bool(0.5) { 1 << 31 } else { 0 };
|
||||||
let exponent = (rng.gen_range(0, u8::MAX) as u32) << 23;
|
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)
|
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
|
// fn(f32) -> f32
|
||||||
macro_rules! f32_f32 {
|
macro_rules! f32_f32 {
|
||||||
($($intr:ident,)+) => {
|
($($intr:ident,)*) => {
|
||||||
fn f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
fn f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
// MUSL C implementation of the function to test
|
// MUSL C implementation of the function to test
|
||||||
extern "C" {
|
extern "C" {
|
||||||
$(fn $intr(_: f32) -> f32;)+
|
$(fn $intr(_: f32) -> f32;)*
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
@@ -78,18 +86,19 @@ macro_rules! f32_f32 {
|
|||||||
",
|
",
|
||||||
stringify!($intr),
|
stringify!($intr),
|
||||||
cases)?;
|
cases)?;
|
||||||
)+
|
)*
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fn(f32, f32) -> f32
|
||||||
macro_rules! f32f32_f32 {
|
macro_rules! f32f32_f32 {
|
||||||
($($intr:ident,)+) => {
|
($($intr:ident,)*) => {
|
||||||
fn f32f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
fn f32f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
$(fn $intr(_: f32, _: f32) -> f32;)+
|
$(fn $intr(_: f32, _: f32) -> f32;)*
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
@@ -137,18 +146,19 @@ macro_rules! f32f32_f32 {
|
|||||||
",
|
",
|
||||||
stringify!($intr),
|
stringify!($intr),
|
||||||
cases)?;
|
cases)?;
|
||||||
)+
|
)*
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fn(f32, i32) -> f32
|
||||||
macro_rules! f32i32_f32 {
|
macro_rules! f32i32_f32 {
|
||||||
($($intr:ident,)+) => {
|
($($intr:ident,)*) => {
|
||||||
fn f32i32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
fn f32i32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
$(fn $intr(_: f32, _: i32) -> f32;)+
|
$(fn $intr(_: f32, _: i32) -> f32;)*
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
@@ -195,7 +205,185 @@ macro_rules! f32i32_f32 {
|
|||||||
",
|
",
|
||||||
stringify!($intr),
|
stringify!($intr),
|
||||||
cases)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -211,6 +399,9 @@ fn main() -> Result<(), Box<Error>> {
|
|||||||
f32_f32(&mut rng)?;
|
f32_f32(&mut rng)?;
|
||||||
f32f32_f32(&mut rng)?;
|
f32f32_f32(&mut rng)?;
|
||||||
f32i32_f32(&mut rng)?;
|
f32i32_f32(&mut rng)?;
|
||||||
|
f64_f64(&mut rng)?;
|
||||||
|
f64f64_f64(&mut rng)?;
|
||||||
|
f64i32_f64(&mut rng)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -233,3 +424,18 @@ f32f32_f32! {
|
|||||||
f32i32_f32! {
|
f32i32_f32! {
|
||||||
scalbnf,
|
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