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

Commit 38b1828

Browse files
committed
Move most unit tests to the libm-test crate
1 parent 4787bfc commit 38b1828

File tree

17 files changed

+347
-422
lines changed

17 files changed

+347
-422
lines changed

crates/libm-test/tests/system_libm.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ trait Call<F> {
4545
fn call(self, f: F) -> Self::Ret;
4646
}
4747

48-
4948
macro_rules! impl_call {
5049
(($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => {
5150
impl Call<unsafe extern"C" fn($($arg_tys),*) -> $ret_ty> for ($($arg_tys),+) {
@@ -127,7 +126,7 @@ macro_rules! impl_eq_f {
127126
ulps <= ULP_TOL as _
128127
}
129128
}
130-
}
129+
};
131130
}
132131

133132
impl_eq_f!(f32, i32);

crates/libm-test/tests/unit.rs

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
#![cfg(test)]
2+
3+
use libm::*;
4+
#[test]
5+
fn remquo_q_overflow() {
6+
// 0xc000000000000001, 0x04c0000000000004
7+
let _ = remquo(-2.0000000000000004, 8.406091369059082e-286);
8+
}
9+
10+
#[test]
11+
fn ceil_sanity_check() {
12+
assert_eq!(ceil(1.1), 2.0);
13+
assert_eq!(ceil(2.9), 3.0);
14+
}
15+
16+
#[test]
17+
fn atan2_sanity_check() {
18+
use std::f64::consts::PI;
19+
assert_eq!(atan2(0.0, 1.0), 0.0);
20+
assert_eq!(atan2(0.0, -1.0), PI);
21+
assert_eq!(atan2(-0.0, -1.0), -PI);
22+
assert_eq!(atan2(3.0, 2.0), atan(3.0 / 2.0));
23+
assert_eq!(atan2(2.0, -1.0), atan(2.0 / -1.0) + PI);
24+
assert_eq!(atan2(-2.0, -1.0), atan(-2.0 / -1.0) - PI);
25+
}
26+
27+
#[test]
28+
fn floor_overflow() {
29+
assert_eq!(floorf(0.5), 0.0);
30+
}
31+
32+
#[test]
33+
fn trunc_sanity_check() {
34+
assert_eq!(trunc(1.1), 1.0);
35+
}
36+
37+
mod pow {
38+
use libm::*;
39+
use std::f64::consts::{E, PI};
40+
use std::f64::{EPSILON, INFINITY, MAX, MIN, MIN_POSITIVE, NAN, NEG_INFINITY};
41+
42+
const POS_ZERO: &[f64] = &[0.0];
43+
const NEG_ZERO: &[f64] = &[-0.0];
44+
const POS_ONE: &[f64] = &[1.0];
45+
const NEG_ONE: &[f64] = &[-1.0];
46+
const POS_FLOATS: &[f64] = &[99.0 / 70.0, E, PI];
47+
const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -E, -PI];
48+
const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), MIN_POSITIVE, EPSILON];
49+
const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -MIN_POSITIVE, -EPSILON];
50+
const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, MAX];
51+
const NEG_EVENS: &[f64] = &[MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0];
52+
const POS_ODDS: &[f64] = &[3.0, 7.0];
53+
const NEG_ODDS: &[f64] = &[-7.0, -3.0];
54+
const NANS: &[f64] = &[NAN];
55+
const POS_INF: &[f64] = &[INFINITY];
56+
const NEG_INF: &[f64] = &[NEG_INFINITY];
57+
58+
const ALL: &[&[f64]] = &[
59+
POS_ZERO,
60+
NEG_ZERO,
61+
NANS,
62+
NEG_SMALL_FLOATS,
63+
POS_SMALL_FLOATS,
64+
NEG_FLOATS,
65+
POS_FLOATS,
66+
NEG_EVENS,
67+
POS_EVENS,
68+
NEG_ODDS,
69+
POS_ODDS,
70+
NEG_INF,
71+
POS_INF,
72+
NEG_ONE,
73+
POS_ONE,
74+
];
75+
const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF];
76+
const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF];
77+
78+
fn pow_test(base: f64, exponent: f64, expected: f64) {
79+
let res = pow(base, exponent);
80+
assert!(
81+
if expected.is_nan() {
82+
res.is_nan()
83+
} else {
84+
pow(base, exponent) == expected
85+
},
86+
"{} ** {} was {} instead of {}",
87+
base,
88+
exponent,
89+
res,
90+
expected
91+
);
92+
}
93+
94+
fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) {
95+
sets.iter()
96+
.for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected)));
97+
}
98+
99+
fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) {
100+
sets.iter()
101+
.for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected)));
102+
}
103+
104+
fn test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) {
105+
sets.iter().for_each(|s| {
106+
s.iter().for_each(|val| {
107+
let exp = expected(*val);
108+
let res = computed(*val);
109+
110+
assert!(
111+
if exp.is_nan() {
112+
res.is_nan()
113+
} else {
114+
exp == res
115+
},
116+
"test for {} was {} instead of {}",
117+
val,
118+
res,
119+
exp
120+
);
121+
})
122+
});
123+
}
124+
125+
#[test]
126+
fn zero_as_exponent() {
127+
test_sets_as_base(ALL, 0.0, 1.0);
128+
test_sets_as_base(ALL, -0.0, 1.0);
129+
}
130+
131+
#[test]
132+
fn one_as_base() {
133+
test_sets_as_exponent(1.0, ALL, 1.0);
134+
}
135+
136+
#[test]
137+
fn nan_inputs() {
138+
// NAN as the base:
139+
// (NAN ^ anything *but 0* should be NAN)
140+
test_sets_as_exponent(NAN, &ALL[2..], NAN);
141+
142+
// NAN as the exponent:
143+
// (anything *but 1* ^ NAN should be NAN)
144+
test_sets_as_base(&ALL[..(ALL.len() - 2)], NAN, NAN);
145+
}
146+
147+
#[test]
148+
fn infinity_as_base() {
149+
// Positive Infinity as the base:
150+
// (+Infinity ^ positive anything but 0 and NAN should be +Infinity)
151+
test_sets_as_exponent(INFINITY, &POS[1..], INFINITY);
152+
153+
// (+Infinity ^ negative anything except 0 and NAN should be 0.0)
154+
test_sets_as_exponent(INFINITY, &NEG[1..], 0.0);
155+
156+
// Negative Infinity as the base:
157+
// (-Infinity ^ positive odd ints should be -Infinity)
158+
test_sets_as_exponent(NEG_INFINITY, &[POS_ODDS], NEG_INFINITY);
159+
160+
// (-Infinity ^ anything but odd ints should be == -0 ^ (-anything))
161+
// We can lump in pos/neg odd ints here because they don't seem to
162+
// cause panics (div by zero) in release mode (I think).
163+
test_sets(ALL, &|v: f64| pow(NEG_INFINITY, v), &|v: f64| pow(-0.0, -v));
164+
}
165+
166+
#[test]
167+
fn infinity_as_exponent() {
168+
// Positive/Negative base greater than 1:
169+
// (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base)
170+
test_sets_as_base(&ALL[5..(ALL.len() - 2)], INFINITY, INFINITY);
171+
172+
// (pos/neg > 1 ^ -Infinity should be 0.0)
173+
test_sets_as_base(&ALL[5..ALL.len() - 2], NEG_INFINITY, 0.0);
174+
175+
// Positive/Negative base less than 1:
176+
let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS];
177+
178+
// (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base)
179+
test_sets_as_base(base_below_one, INFINITY, 0.0);
180+
181+
// (pos/neg < 1 ^ -Infinity should be Infinity)
182+
test_sets_as_base(base_below_one, NEG_INFINITY, INFINITY);
183+
184+
// Positive/Negative 1 as the base:
185+
// (pos/neg 1 ^ Infinity should be 1)
186+
test_sets_as_base(&[NEG_ONE, POS_ONE], INFINITY, 1.0);
187+
188+
// (pos/neg 1 ^ -Infinity should be 1)
189+
test_sets_as_base(&[NEG_ONE, POS_ONE], NEG_INFINITY, 1.0);
190+
}
191+
192+
#[test]
193+
fn zero_as_base() {
194+
// Positive Zero as the base:
195+
// (+0 ^ anything positive but 0 and NAN should be +0)
196+
test_sets_as_exponent(0.0, &POS[1..], 0.0);
197+
198+
// (+0 ^ anything negative but 0 and NAN should be Infinity)
199+
// (this should panic because we're dividing by zero)
200+
test_sets_as_exponent(0.0, &NEG[1..], INFINITY);
201+
202+
// Negative Zero as the base:
203+
// (-0 ^ anything positive but 0, NAN, and odd ints should be +0)
204+
test_sets_as_exponent(-0.0, &POS[3..], 0.0);
205+
206+
// (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity)
207+
// (should panic because of divide by zero)
208+
test_sets_as_exponent(-0.0, &NEG[3..], INFINITY);
209+
210+
// (-0 ^ positive odd ints should be -0)
211+
test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0);
212+
213+
// (-0 ^ negative odd ints should be -Infinity)
214+
// (should panic because of divide by zero)
215+
test_sets_as_exponent(-0.0, &[NEG_ODDS], NEG_INFINITY);
216+
}
217+
218+
#[test]
219+
fn special_cases() {
220+
// One as the exponent:
221+
// (anything ^ 1 should be anything - i.e. the base)
222+
test_sets(ALL, &|v: f64| pow(v, 1.0), &|v: f64| v);
223+
224+
// Negative One as the exponent:
225+
// (anything ^ -1 should be 1/anything)
226+
test_sets(ALL, &|v: f64| pow(v, -1.0), &|v: f64| 1.0 / v);
227+
228+
// Factoring -1 out:
229+
// (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer))
230+
&[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS]
231+
.iter()
232+
.for_each(|int_set| {
233+
int_set.iter().for_each(|int| {
234+
test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| {
235+
pow(-1.0, *int) * pow(v, *int)
236+
});
237+
})
238+
});
239+
240+
// Negative base (imaginary results):
241+
// (-anything except 0 and Infinity ^ non-integer should be NAN)
242+
&NEG[1..(NEG.len() - 1)].iter().for_each(|set| {
243+
set.iter().for_each(|val| {
244+
test_sets(&ALL[3..7], &|v: f64| pow(*val, v), &|_| NAN);
245+
})
246+
});
247+
}
248+
249+
#[test]
250+
fn normal_cases() {
251+
assert_eq!(pow(2.0, 20.0), (1 << 20) as f64);
252+
assert_eq!(pow(-1.0, 9.0), -1.0);
253+
assert!(pow(-1.0, 2.2).is_nan());
254+
assert!(pow(-1.0, -1.14).is_nan());
255+
}
256+
}
257+
258+
mod atan {
259+
use libm::atan;
260+
use std::f64;
261+
262+
#[test]
263+
fn sanity_check() {
264+
for (input, answer) in [
265+
(3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6),
266+
(1.0, f64::consts::FRAC_PI_4),
267+
(3.0_f64.sqrt(), f64::consts::FRAC_PI_3),
268+
(-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6),
269+
(-1.0, -f64::consts::FRAC_PI_4),
270+
(-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3),
271+
]
272+
.iter()
273+
{
274+
assert!(
275+
(atan(*input) - answer) / answer < 1e-5,
276+
"\natan({:.4}/16) = {:.4}, actual: {}",
277+
input * 16.0,
278+
answer,
279+
atan(*input)
280+
);
281+
}
282+
}
283+
284+
#[test]
285+
fn zero() {
286+
assert_eq!(atan(0.0), 0.0);
287+
}
288+
289+
#[test]
290+
fn infinity() {
291+
assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2);
292+
}
293+
294+
#[test]
295+
fn minus_infinity() {
296+
assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2);
297+
}
298+
299+
#[test]
300+
fn nan() {
301+
assert!(atan(f64::NAN).is_nan());
302+
}
303+
}
304+
305+
#[test]
306+
fn sin_near_pi() {
307+
let x = f64::from_bits(0x400921fb000FD5DD); // 3.141592026217707
308+
let sx = f64::from_bits(0x3ea50d15ced1a4a2); // 6.273720864039205e-7
309+
assert_eq!(sin(x), sx);
310+
}
311+
312+
#[test]
313+
fn truncf_sanity_check() {
314+
assert_eq!(truncf(1.1), 1.0);
315+
}
316+
317+
#[test]
318+
fn expm1_sanity_check() {
319+
assert_eq!(expm1(1.1), 2.0041660239464334);
320+
}
321+
322+
#[test]
323+
fn roundf_negative_zero() {
324+
assert_eq!(roundf(-0.0_f32).to_bits(), (-0.0_f32).to_bits());
325+
}
326+
327+
#[test]
328+
fn exp2_i0_wrap_test() {
329+
let x = -3.0 / 256.0;
330+
assert_eq!(exp2(x), f64::from_bits(0x3fefbdba3692d514));
331+
}
332+
333+
#[test]
334+
fn round_negative_zero() {
335+
assert_eq!(round(-0.0_f64).to_bits(), (-0.0_f64).to_bits());
336+
}
337+
338+
#[test]
339+
fn j1f_2488() {
340+
// 0x401F3E49
341+
assert_eq!(j1f(2.4881766_f32), 0.49999475_f32);
342+
}
343+
#[test]
344+
fn y1f_2002() {
345+
assert_eq!(y1f(2.0000002_f32), -0.10703229_f32);
346+
}

0 commit comments

Comments
 (0)