Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit 28d7851

Browse files
committed
libm-test: expands tests for all libm APIs
Currently, only the randomly generated tests from the old build.rs are generated.
1 parent fa2dde0 commit 28d7851

File tree

6 files changed

+168
-14
lines changed

6 files changed

+168
-14
lines changed

crates/libm-test/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "libm-test"
3+
version = "0.1.0"
4+
authors = ["Gonzalo Brito Gadeschi <[email protected]>"]
5+
edition = "2018"
6+
7+
[dev-dependencies]
8+
libm = { path = "../libm" }
9+
libm-analyze = { path = "../libm-analyze", no-default-features = true }
10+
rand = "0.7"
11+
12+
[features]
13+
default = []
14+
checked = ["libm/checked"]

crates/libm-test/build.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use std::env;
2+
3+
fn main() {
4+
println!("cargo:rerun-if-changed=build.rs");
5+
6+
if !cfg!(feature = "checked") {
7+
let lvl = env::var("OPT_LEVEL").unwrap();
8+
if lvl != "0" {
9+
println!("cargo:rustc-cfg=assert_no_panic");
10+
}
11+
}
12+
}

crates/libm-test/tests/system_libm.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Compare the results of the `libm` implementation against the system's libm.
2+
#![cfg(test)]
3+
4+
// Number of tests to generate for each function
5+
const NTESTS: usize = 500;
6+
7+
// FIXME: should be 1
8+
const ULP_TOL: usize = 3;
9+
10+
macro_rules! system_libm {
11+
(
12+
id: $id:ident;
13+
arg_tys: $($arg_tys:ty),*;
14+
arg_ids: $($arg_ids:ident),*;
15+
ret: $ret_ty:ty;
16+
) => {
17+
#[test]
18+
#[allow(unused)]
19+
fn $id() {
20+
use crate::Call;
21+
let mut rng = rand::thread_rng();
22+
for _ in 0..NTESTS {
23+
let args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ );
24+
extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty {
25+
libm::$id($($arg_ids),*)
26+
}
27+
let result = args.call(libm_fn);
28+
extern "C" {
29+
fn $id($($arg_ids: $arg_tys),*) -> $ret_ty;
30+
}
31+
let expected = args.call($id);
32+
if !result.eq(expected) {
33+
eprintln!("result = {} != {} (expected)", result, expected);
34+
panic!();
35+
}
36+
}
37+
}
38+
}
39+
}
40+
41+
libm_analyze::for_each_api!(system_libm);
42+
43+
trait Call<F> {
44+
type Ret;
45+
fn call(self, f: F) -> Self::Ret;
46+
}
47+
48+
49+
macro_rules! impl_call {
50+
(($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => {
51+
impl Call<unsafe extern"C" fn($($arg_tys),*) -> $ret_ty> for ($($arg_tys),+) {
52+
type Ret = $ret_ty;
53+
fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret {
54+
let $self_ = self;
55+
unsafe { f($($xs),*) }
56+
}
57+
}
58+
};
59+
}
60+
61+
impl_call!((f32) -> f32: x: x);
62+
impl_call!((f32,f32) -> f32: x: x.0, x.1);
63+
impl_call!((f32,f32,f32) -> f32: x: x.0, x.1, x.2);
64+
impl_call!((f64) -> f64: x: x);
65+
impl_call!((f64,f64) -> f64: x: x.0, x.1);
66+
impl_call!((f64,f64,f64) -> f64: x: x.0, x.1, x.2);
67+
68+
impl_call!((f64, i32) -> f64: x: x.0, x.1);
69+
impl_call!((f32, i32) -> f32: x: x.0, x.1);
70+
71+
trait Rand {
72+
fn gen(rng: &mut rand::rngs::ThreadRng) -> Self;
73+
}
74+
75+
macro_rules! impl_rand {
76+
($id:ident: [$($e:expr),*]) => {
77+
impl Rand for $id {
78+
fn gen(r: &mut rand::rngs::ThreadRng) -> Self {
79+
use rand::Rng;
80+
use rand::seq::SliceRandom;
81+
let r = if r.gen_range(0, 20) < 1 {
82+
*[$($e),*].choose(r).unwrap()
83+
} else {
84+
r.gen::<$id>()
85+
};
86+
unsafe { std::mem::transmute(r) }
87+
}
88+
}
89+
}
90+
}
91+
92+
impl_rand!(f32: [std::f32::NAN, std::f32::INFINITY, std::f32::NEG_INFINITY]);
93+
impl_rand!(f64: [std::f64::NAN, std::f64::INFINITY, std::f64::NEG_INFINITY]);
94+
impl_rand!(i32: [i32::max_value(), 0_i32, i32::min_value()]);
95+
96+
trait Equal {
97+
fn eq(self, other: Self) -> bool;
98+
}
99+
100+
macro_rules! impl_eq_f {
101+
($f_ty:ty, $i_ty:ty) => {
102+
impl Equal for $f_ty {
103+
fn eq(self, y: $f_ty) -> bool {
104+
let x = self;
105+
if x.is_nan() != y.is_nan() {
106+
// one is nan but the other is not
107+
return false;
108+
}
109+
if x.is_nan() && y.is_nan() {
110+
return true;
111+
}
112+
if x.is_infinite() != y.is_infinite() {
113+
// one is inf but the other is not
114+
return false;
115+
}
116+
if x.is_infinite() != y.is_infinite() {
117+
// one is inf but the other is not
118+
return false;
119+
}
120+
let xi: $i_ty = unsafe { core::intrinsics::transmute(x) };
121+
let yi: $i_ty = unsafe { core::intrinsics::transmute(y) };
122+
if (xi < 0) != (yi < 0) {
123+
// different sign
124+
return false;
125+
}
126+
let ulps = (xi - yi).abs();
127+
ulps <= ULP_TOL as _
128+
}
129+
}
130+
}
131+
}
132+
133+
impl_eq_f!(f32, i32);
134+
impl_eq_f!(f64, i64);
135+
136+
impl Equal for i32 {
137+
fn eq(self, y: i32) -> bool {
138+
let x = self;
139+
let ulps = (x - y).abs();
140+
ulps <= 1
141+
}
142+
}

crates/libm-verify/Cargo.toml

Lines changed: 0 additions & 10 deletions
This file was deleted.

crates/libm-verify/src/lib.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

crates/libm-verify/tests/musl.rs

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)