Add tests for edge cases
Introduce a generator that will tests various points of interest including zeros, infinities, and NaNs.
This commit is contained in:
parent
a8a2f70ae6
commit
13611a1b76
4 changed files with 101 additions and 3 deletions
|
|
@ -3,7 +3,7 @@
|
|||
use std::fmt;
|
||||
use std::ops::{self, Bound};
|
||||
|
||||
use crate::Float;
|
||||
use crate::{Float, FloatExt};
|
||||
|
||||
/// Representation of a function's domain.
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -19,7 +19,7 @@ pub struct Domain<T> {
|
|||
|
||||
type BoxIter<T> = Box<dyn Iterator<Item = T>>;
|
||||
|
||||
impl<F: Float> Domain<F> {
|
||||
impl<F: FloatExt> Domain<F> {
|
||||
/// The start of this domain, saturating at negative infinity.
|
||||
pub fn range_start(&self) -> F {
|
||||
match self.start {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use crate::GenerateInput;
|
||||
pub mod domain_logspace;
|
||||
pub mod edge_cases;
|
||||
pub mod random;
|
||||
|
||||
/// Helper type to turn any reusable input into a generator.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
//! A generator that checks a handful of cases near infinities, zeros, asymptotes, and NaNs.
|
||||
|
||||
use libm::support::Float;
|
||||
|
||||
use crate::domain::HasDomain;
|
||||
use crate::{FloatExt, MathOp};
|
||||
|
||||
/// Number of values near an interesting point to check.
|
||||
// FIXME(ntests): replace this with a more logical algorithm
|
||||
const AROUND: usize = 100;
|
||||
|
||||
/// Functions have infinite asymptotes, limit how many we check.
|
||||
// FIXME(ntests): replace this with a more logical algorithm
|
||||
const MAX_CHECK_POINTS: usize = 10;
|
||||
|
||||
/// Create a list of values around interesting points (infinities, zeroes, NaNs).
|
||||
pub fn get_test_cases<Op, F>() -> impl Iterator<Item = (F,)>
|
||||
where
|
||||
Op: MathOp<FTy = F> + HasDomain<F>,
|
||||
F: Float,
|
||||
{
|
||||
let mut ret = Vec::new();
|
||||
let values = &mut ret;
|
||||
let domain = Op::DOMAIN;
|
||||
let domain_start = domain.range_start();
|
||||
let domain_end = domain.range_end();
|
||||
|
||||
// Check near some notable constants
|
||||
count_up(F::ONE, values);
|
||||
count_up(F::ZERO, values);
|
||||
count_up(F::NEG_ONE, values);
|
||||
count_down(F::ONE, values);
|
||||
count_down(F::ZERO, values);
|
||||
count_down(F::NEG_ONE, values);
|
||||
values.push(F::NEG_ZERO);
|
||||
|
||||
// Check values near the extremes
|
||||
count_up(F::NEG_INFINITY, values);
|
||||
count_down(F::INFINITY, values);
|
||||
count_down(domain_end, values);
|
||||
count_up(domain_start, values);
|
||||
count_down(domain_start, values);
|
||||
count_up(domain_end, values);
|
||||
count_down(domain_end, values);
|
||||
|
||||
// Check some special values that aren't included in the above ranges
|
||||
values.push(F::NAN);
|
||||
values.extend(F::consts().iter());
|
||||
|
||||
// Check around asymptotes
|
||||
if let Some(f) = domain.check_points {
|
||||
let iter = f();
|
||||
for x in iter.take(MAX_CHECK_POINTS) {
|
||||
count_up(x, values);
|
||||
count_down(x, values);
|
||||
}
|
||||
}
|
||||
|
||||
// Some results may overlap so deduplicate the vector to save test cycles.
|
||||
values.sort_by_key(|x| x.to_bits());
|
||||
values.dedup_by_key(|x| x.to_bits());
|
||||
|
||||
ret.into_iter().map(|v| (v,))
|
||||
}
|
||||
|
||||
/// Add `AROUND` values starting at and including `x` and counting up. Uses the smallest possible
|
||||
/// increments (1 ULP).
|
||||
fn count_up<F: Float>(mut x: F, values: &mut Vec<F>) {
|
||||
assert!(!x.is_nan());
|
||||
|
||||
let mut count = 0;
|
||||
while x < F::INFINITY && count < AROUND {
|
||||
values.push(x);
|
||||
x = x.next_up();
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Add `AROUND` values starting at and including `x` and counting down. Uses the smallest possible
|
||||
/// increments (1 ULP).
|
||||
fn count_down<F: Float>(mut x: F, values: &mut Vec<F>) {
|
||||
assert!(!x.is_nan());
|
||||
|
||||
let mut count = 0;
|
||||
while x > F::NEG_INFINITY && count < AROUND {
|
||||
values.push(x);
|
||||
x = x.next_down();
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#![cfg(feature = "test-multiprecision")]
|
||||
|
||||
use libm_test::domain::HasDomain;
|
||||
use libm_test::gen::{CachedInput, domain_logspace, random};
|
||||
use libm_test::gen::{CachedInput, domain_logspace, edge_cases, random};
|
||||
use libm_test::mpfloat::MpOp;
|
||||
use libm_test::{
|
||||
CheckBasis, CheckCtx, CheckOutput, GenerateInput, MathOp, OpFTy, OpRustFn, OpRustRet, TupleCall,
|
||||
|
|
@ -79,6 +79,13 @@ macro_rules! mp_domain_tests {
|
|||
attrs: [$($meta:meta)*]
|
||||
) => {
|
||||
paste::paste! {
|
||||
#[test]
|
||||
$(#[$meta])*
|
||||
fn [< mp_edge_case_ $fn_name >]() {
|
||||
type Op = libm_test::op::$fn_name::Routine;
|
||||
domain_test_runner::<Op>(edge_cases::get_test_cases::<Op, _>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
$(#[$meta])*
|
||||
fn [< mp_logspace_ $fn_name >]() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue