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

Commit a4cfd44

Browse files
committed
Add tests for edge cases
Introduce a generator that will tests various points of interest including zeros, infinities, and NaNs.
1 parent 34b6e2f commit a4cfd44

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

crates/libm-test/src/gen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use crate::GenerateInput;
44
pub mod domain_logspace;
5+
pub mod edge_cases;
56
pub mod random;
67

78
/// Helper type to turn any reusable input into a generator.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//! A generator that checks a handful of cases near infinities, zeros, asymptotes, and NaNs.
2+
3+
use libm::support::Float;
4+
5+
use crate::domain::{Domain, HasDomain};
6+
use crate::{FloatExt, MathOp};
7+
8+
/// Number of values near an interesting point to check.
9+
// FIXME(ntests): replace this with a more logical algorithm
10+
const AROUND: usize = 100;
11+
12+
/// Functions have infinite asymptotes, limit how many we check.
13+
const MAX_CHECK_POINTS: usize = 10;
14+
15+
/// Create a list of values around interesting points (infinities, zeroes, NaNs).
16+
pub fn get_test_cases<Op>() -> impl Iterator<Item = (Op::FTy,)>
17+
where
18+
Op: MathOp + HasDomain<Op::FTy>,
19+
{
20+
let mut values = Vec::new();
21+
populate_values::<Op::FTy>(&mut values, Op::D);
22+
values.sort_by_key(|x| x.to_bits());
23+
values.dedup_by_key(|x| x.to_bits());
24+
values.into_iter().map(|v| (v,))
25+
}
26+
27+
fn populate_values<F: Float>(values: &mut Vec<F>, domain: Domain<F>) {
28+
// Check near some notable constants
29+
count_up(F::ONE, values);
30+
count_up(F::ZERO, values);
31+
count_up(F::NEG_ONE, values);
32+
count_down(F::ONE, values);
33+
count_down(F::ZERO, values);
34+
count_down(F::NEG_ONE, values);
35+
values.push(F::NEG_ZERO);
36+
37+
// Check values near the extremes
38+
values.push(F::INFINITY);
39+
values.push(F::NEG_INFINITY);
40+
values.push(F::MIN);
41+
values.push(F::MAX);
42+
count_up(F::MIN, values);
43+
count_down(F::MAX, values);
44+
45+
// Check some special values that aren't included in the above ranges
46+
values.push(F::NAN);
47+
values.extend(F::consts().iter());
48+
49+
// Check around asymptotest
50+
if let Some(f) = domain.check_points {
51+
let iter = f();
52+
for x in iter.take(MAX_CHECK_POINTS) {
53+
count_up(x, values);
54+
count_down(x, values);
55+
}
56+
}
57+
}
58+
59+
/// Add `AROUND` values starting at `x` and counting up, using the smallest possible increments
60+
/// (1 ULP).
61+
fn count_up<F: Float>(mut x: F, values: &mut Vec<F>) {
62+
assert!(!x.is_nan());
63+
assert!(!x.is_infinite());
64+
65+
let mut count = 0;
66+
while !x.is_infinite() && count < AROUND {
67+
values.push(x);
68+
x = x.next_up();
69+
count += 1;
70+
}
71+
}
72+
73+
/// Add `AROUND` values starting at `x` and counting down, using the smallest possible increments
74+
/// (1 ULP).
75+
fn count_down<F: Float>(mut x: F, values: &mut Vec<F>) {
76+
assert!(!x.is_nan());
77+
assert!(!x.is_infinite());
78+
79+
let mut count = 0;
80+
while !x.is_infinite() && count < AROUND {
81+
values.push(x);
82+
x = x.next_down();
83+
count += 1;
84+
}
85+
}

crates/libm-test/tests/multiprecision.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![cfg(feature = "test-multiprecision")]
44

55
use libm_test::domain::HasDomain;
6-
use libm_test::gen::{CachedInput, domain_logspace, random};
6+
use libm_test::gen::{CachedInput, domain_logspace, edge_cases, random};
77
use libm_test::mpfloat::MpOp;
88
use libm_test::{CheckBasis, CheckCtx, CheckOutput, GenerateInput, MathOp, TupleCall};
99

@@ -77,6 +77,13 @@ macro_rules! mp_domain_tests {
7777
attrs: [$($meta:meta)*]
7878
) => {
7979
paste::paste! {
80+
#[test]
81+
$(#[$meta])*
82+
fn [< mp_edge_case_ $fn_name >]() {
83+
type Op = libm_test::op::$fn_name::Routine;
84+
domain_test_runner::<Op>(edge_cases::get_test_cases::<Op>());
85+
}
86+
8087
#[test]
8188
$(#[$meta])*
8289
fn [< mp_logspace_ $fn_name >]() {

0 commit comments

Comments
 (0)