blob: 43b28722f2dd243729d3c6d539aaf822d63b2e59 [file] [log] [blame]
//! Test cases to verify specific values.
//!
//! Each routine can have a set of inputs and, optinoally, outputs. If an output is provided, it
//! will be used to check against. If only inputs are provided, the case will be checked against
//! a basis.
//!
//! This is useful for adding regression tests or expected failures.
use libm::hf64;
#[cfg(f128_enabled)]
use libm::hf128;
use crate::{CheckBasis, CheckCtx, GeneratorKind, MathOp, op};
pub struct TestCase<Op: MathOp> {
pub input: Op::RustArgs,
pub output: Option<Op::RustRet>,
}
impl<Op: MathOp> TestCase<Op> {
#[expect(dead_code)]
fn append_inputs(v: &mut Vec<Self>, l: &[Op::RustArgs]) {
v.extend(l.iter().copied().map(|input| Self {
input,
output: None,
}));
}
fn append_pairs(v: &mut Vec<Self>, l: &[(Op::RustArgs, Option<Op::RustRet>)])
where
Op::RustRet: Copy,
{
v.extend(
l.iter()
.copied()
.map(|(input, output)| Self { input, output }),
);
}
}
fn acos_cases() -> Vec<TestCase<op::acos::Routine>> {
vec![]
}
fn acosf_cases() -> Vec<TestCase<op::acosf::Routine>> {
vec![]
}
fn acosh_cases() -> Vec<TestCase<op::acosh::Routine>> {
vec![]
}
fn acoshf_cases() -> Vec<TestCase<op::acoshf::Routine>> {
vec![]
}
fn asin_cases() -> Vec<TestCase<op::asin::Routine>> {
vec![]
}
fn asinf_cases() -> Vec<TestCase<op::asinf::Routine>> {
vec![]
}
fn asinh_cases() -> Vec<TestCase<op::asinh::Routine>> {
vec![]
}
fn asinhf_cases() -> Vec<TestCase<op::asinhf::Routine>> {
vec![]
}
fn atan_cases() -> Vec<TestCase<op::atan::Routine>> {
vec![]
}
fn atan2_cases() -> Vec<TestCase<op::atan2::Routine>> {
vec![]
}
fn atan2f_cases() -> Vec<TestCase<op::atan2f::Routine>> {
vec![]
}
fn atanf_cases() -> Vec<TestCase<op::atanf::Routine>> {
vec![]
}
fn atanh_cases() -> Vec<TestCase<op::atanh::Routine>> {
vec![]
}
fn atanhf_cases() -> Vec<TestCase<op::atanhf::Routine>> {
vec![]
}
fn cbrt_cases() -> Vec<TestCase<op::cbrt::Routine>> {
vec![]
}
fn cbrtf_cases() -> Vec<TestCase<op::cbrtf::Routine>> {
vec![]
}
fn ceil_cases() -> Vec<TestCase<op::ceil::Routine>> {
vec![]
}
fn ceilf_cases() -> Vec<TestCase<op::ceilf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn ceilf128_cases() -> Vec<TestCase<op::ceilf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn ceilf16_cases() -> Vec<TestCase<op::ceilf16::Routine>> {
vec![]
}
fn copysign_cases() -> Vec<TestCase<op::copysign::Routine>> {
vec![]
}
fn copysignf_cases() -> Vec<TestCase<op::copysignf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn copysignf128_cases() -> Vec<TestCase<op::copysignf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn copysignf16_cases() -> Vec<TestCase<op::copysignf16::Routine>> {
vec![]
}
fn cos_cases() -> Vec<TestCase<op::cos::Routine>> {
vec![]
}
fn cosf_cases() -> Vec<TestCase<op::cosf::Routine>> {
vec![]
}
fn cosh_cases() -> Vec<TestCase<op::cosh::Routine>> {
vec![]
}
fn coshf_cases() -> Vec<TestCase<op::coshf::Routine>> {
vec![]
}
fn erf_cases() -> Vec<TestCase<op::erf::Routine>> {
vec![]
}
fn erfc_cases() -> Vec<TestCase<op::erfc::Routine>> {
vec![]
}
fn erfcf_cases() -> Vec<TestCase<op::erfcf::Routine>> {
vec![]
}
fn erff_cases() -> Vec<TestCase<op::erff::Routine>> {
vec![]
}
fn exp_cases() -> Vec<TestCase<op::exp::Routine>> {
vec![]
}
fn exp10_cases() -> Vec<TestCase<op::exp10::Routine>> {
vec![]
}
fn exp10f_cases() -> Vec<TestCase<op::exp10f::Routine>> {
vec![]
}
fn exp2_cases() -> Vec<TestCase<op::exp2::Routine>> {
vec![]
}
fn exp2f_cases() -> Vec<TestCase<op::exp2f::Routine>> {
vec![]
}
fn expf_cases() -> Vec<TestCase<op::expf::Routine>> {
vec![]
}
fn expm1_cases() -> Vec<TestCase<op::expm1::Routine>> {
vec![]
}
fn expm1f_cases() -> Vec<TestCase<op::expm1f::Routine>> {
vec![]
}
fn fabs_cases() -> Vec<TestCase<op::fabs::Routine>> {
vec![]
}
fn fabsf_cases() -> Vec<TestCase<op::fabsf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fabsf128_cases() -> Vec<TestCase<op::fabsf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fabsf16_cases() -> Vec<TestCase<op::fabsf16::Routine>> {
vec![]
}
fn fdim_cases() -> Vec<TestCase<op::fdim::Routine>> {
vec![]
}
fn fdimf_cases() -> Vec<TestCase<op::fdimf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fdimf128_cases() -> Vec<TestCase<op::fdimf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fdimf16_cases() -> Vec<TestCase<op::fdimf16::Routine>> {
vec![]
}
fn floor_cases() -> Vec<TestCase<op::floor::Routine>> {
vec![]
}
fn floorf_cases() -> Vec<TestCase<op::floorf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn floorf128_cases() -> Vec<TestCase<op::floorf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn floorf16_cases() -> Vec<TestCase<op::floorf16::Routine>> {
vec![]
}
fn fma_cases() -> Vec<TestCase<op::fma::Routine>> {
let mut v = vec![];
TestCase::append_pairs(
&mut v,
&[
// Previous failure with incorrect sign
((5e-324, -5e-324, 0.0), Some(-0.0)),
],
);
v
}
fn fmaf_cases() -> Vec<TestCase<op::fmaf::Routine>> {
let mut v = vec![];
TestCase::append_pairs(
&mut v,
&[
// Known rounding error for some implementations (notably MinGW)
(
(-1.9369631e13f32, 2.1513551e-7, -1.7354427e-24),
Some(-4167095.8),
),
],
);
v
}
#[cfg(f128_enabled)]
fn fmaf128_cases() -> Vec<TestCase<op::fmaf128::Routine>> {
let mut v = vec![];
TestCase::append_pairs(
&mut v,
&[
(
// Tricky rounding case that previously failed in extensive tests
(
hf128!("-0x1.1966cc01966cc01966cc01966f06p-25"),
hf128!("-0x1.669933fe69933fe69933fe6997c9p-16358"),
hf128!("-0x0.000000000000000000000000048ap-16382"),
),
Some(hf128!("0x0.c5171470a3ff5e0f68d751491b18p-16382")),
),
(
// Subnormal edge case that caused a failure
(
hf128!("0x0.7ffffffffffffffffffffffffff7p-16382"),
hf128!("0x1.ffffffffffffffffffffffffffffp-1"),
hf128!("0x0.8000000000000000000000000009p-16382"),
),
Some(hf128!("0x1.0000000000000000000000000000p-16382")),
),
],
);
v
}
#[cfg(f16_enabled)]
fn fmaxf16_cases() -> Vec<TestCase<op::fmaxf16::Routine>> {
vec![]
}
fn fmaxf_cases() -> Vec<TestCase<op::fmaxf::Routine>> {
vec![]
}
fn fmax_cases() -> Vec<TestCase<op::fmax::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fmaxf128_cases() -> Vec<TestCase<op::fmaxf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fmaximumf16_cases() -> Vec<TestCase<op::fmaximumf16::Routine>> {
vec![]
}
fn fmaximumf_cases() -> Vec<TestCase<op::fmaximumf::Routine>> {
vec![]
}
fn fmaximum_cases() -> Vec<TestCase<op::fmaximum::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fmaximumf128_cases() -> Vec<TestCase<op::fmaximumf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fmaximum_numf16_cases() -> Vec<TestCase<op::fmaximum_numf16::Routine>> {
vec![]
}
fn fmaximum_numf_cases() -> Vec<TestCase<op::fmaximum_numf::Routine>> {
vec![]
}
fn fmaximum_num_cases() -> Vec<TestCase<op::fmaximum_num::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fmaximum_numf128_cases() -> Vec<TestCase<op::fmaximum_numf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fminf16_cases() -> Vec<TestCase<op::fminf16::Routine>> {
vec![]
}
fn fminf_cases() -> Vec<TestCase<op::fminf::Routine>> {
vec![]
}
fn fmin_cases() -> Vec<TestCase<op::fmin::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fminf128_cases() -> Vec<TestCase<op::fminf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fminimumf16_cases() -> Vec<TestCase<op::fminimumf16::Routine>> {
vec![]
}
fn fminimumf_cases() -> Vec<TestCase<op::fminimumf::Routine>> {
vec![]
}
fn fminimum_cases() -> Vec<TestCase<op::fminimum::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fminimumf128_cases() -> Vec<TestCase<op::fminimumf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fminimum_numf16_cases() -> Vec<TestCase<op::fminimum_numf16::Routine>> {
vec![]
}
fn fminimum_numf_cases() -> Vec<TestCase<op::fminimum_numf::Routine>> {
vec![]
}
fn fminimum_num_cases() -> Vec<TestCase<op::fminimum_num::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn fminimum_numf128_cases() -> Vec<TestCase<op::fminimum_numf128::Routine>> {
vec![]
}
fn fmod_cases() -> Vec<TestCase<op::fmod::Routine>> {
let mut v = vec![];
TestCase::append_pairs(
&mut v,
&[
// Previous failure with incorrect loop iteration
// <https://github.com/rust-lang/libm/pull/469#discussion_r2022337272>
((2.1, 3.123e-320), Some(2.0696e-320)),
((2.1, 2.253547e-318), Some(1.772535e-318)),
],
);
v
}
fn fmodf_cases() -> Vec<TestCase<op::fmodf::Routine>> {
let mut v = vec![];
TestCase::append_pairs(
&mut v,
&[
// Previous failure with incorrect loop iteration
// <https://github.com/rust-lang/libm/pull/469#discussion_r2022337272>
((2.1, 8.858e-42), Some(8.085e-42)),
((2.1, 6.39164e-40), Some(6.1636e-40)),
((5.5, 6.39164e-40), Some(4.77036e-40)),
((-151.189, 6.39164e-40), Some(-5.64734e-40)),
],
);
v
}
#[cfg(f128_enabled)]
fn fmodf128_cases() -> Vec<TestCase<op::fmodf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn fmodf16_cases() -> Vec<TestCase<op::fmodf16::Routine>> {
vec![]
}
fn frexp_cases() -> Vec<TestCase<op::frexp::Routine>> {
vec![]
}
fn frexpf_cases() -> Vec<TestCase<op::frexpf::Routine>> {
vec![]
}
fn hypot_cases() -> Vec<TestCase<op::hypot::Routine>> {
vec![]
}
fn hypotf_cases() -> Vec<TestCase<op::hypotf::Routine>> {
vec![]
}
fn ilogb_cases() -> Vec<TestCase<op::ilogb::Routine>> {
vec![]
}
fn ilogbf_cases() -> Vec<TestCase<op::ilogbf::Routine>> {
vec![]
}
fn j0_cases() -> Vec<TestCase<op::j0::Routine>> {
vec![]
}
fn j0f_cases() -> Vec<TestCase<op::j0f::Routine>> {
vec![]
}
fn j1_cases() -> Vec<TestCase<op::j1::Routine>> {
vec![]
}
fn j1f_cases() -> Vec<TestCase<op::j1f::Routine>> {
vec![]
}
fn jn_cases() -> Vec<TestCase<op::jn::Routine>> {
vec![]
}
fn jnf_cases() -> Vec<TestCase<op::jnf::Routine>> {
vec![]
}
fn ldexp_cases() -> Vec<TestCase<op::ldexp::Routine>> {
vec![]
}
fn ldexpf_cases() -> Vec<TestCase<op::ldexpf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn ldexpf128_cases() -> Vec<TestCase<op::ldexpf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn ldexpf16_cases() -> Vec<TestCase<op::ldexpf16::Routine>> {
vec![]
}
fn lgamma_cases() -> Vec<TestCase<op::lgamma::Routine>> {
vec![]
}
fn lgamma_r_cases() -> Vec<TestCase<op::lgamma_r::Routine>> {
vec![]
}
fn lgammaf_cases() -> Vec<TestCase<op::lgammaf::Routine>> {
vec![]
}
fn lgammaf_r_cases() -> Vec<TestCase<op::lgammaf_r::Routine>> {
vec![]
}
fn log_cases() -> Vec<TestCase<op::log::Routine>> {
vec![]
}
fn log10_cases() -> Vec<TestCase<op::log10::Routine>> {
vec![]
}
fn log10f_cases() -> Vec<TestCase<op::log10f::Routine>> {
vec![]
}
fn log1p_cases() -> Vec<TestCase<op::log1p::Routine>> {
vec![]
}
fn log1pf_cases() -> Vec<TestCase<op::log1pf::Routine>> {
vec![]
}
fn log2_cases() -> Vec<TestCase<op::log2::Routine>> {
vec![]
}
fn log2f_cases() -> Vec<TestCase<op::log2f::Routine>> {
vec![]
}
fn logf_cases() -> Vec<TestCase<op::logf::Routine>> {
vec![]
}
fn modf_cases() -> Vec<TestCase<op::modf::Routine>> {
vec![]
}
fn modff_cases() -> Vec<TestCase<op::modff::Routine>> {
vec![]
}
fn nextafter_cases() -> Vec<TestCase<op::nextafter::Routine>> {
vec![]
}
fn nextafterf_cases() -> Vec<TestCase<op::nextafterf::Routine>> {
vec![]
}
fn pow_cases() -> Vec<TestCase<op::pow::Routine>> {
vec![]
}
fn powf_cases() -> Vec<TestCase<op::powf::Routine>> {
vec![]
}
fn remainder_cases() -> Vec<TestCase<op::remainder::Routine>> {
vec![]
}
fn remainderf_cases() -> Vec<TestCase<op::remainderf::Routine>> {
vec![]
}
fn remquo_cases() -> Vec<TestCase<op::remquo::Routine>> {
vec![]
}
fn remquof_cases() -> Vec<TestCase<op::remquof::Routine>> {
vec![]
}
fn rint_cases() -> Vec<TestCase<op::rint::Routine>> {
let mut v = vec![];
TestCase::append_pairs(
&mut v,
&[
// Known failure on i586
#[cfg(not(x86_no_sse))]
(
(hf64!("-0x1.e3f13ff995ffcp+38"),),
Some(hf64!("-0x1.e3f13ff994000p+38")),
),
#[cfg(x86_no_sse)]
(
(hf64!("-0x1.e3f13ff995ffcp+38"),),
Some(hf64!("-0x1.e3f13ff998000p+38")),
),
],
);
v
}
fn rintf_cases() -> Vec<TestCase<op::rintf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn rintf128_cases() -> Vec<TestCase<op::rintf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn rintf16_cases() -> Vec<TestCase<op::rintf16::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn roundf16_cases() -> Vec<TestCase<op::roundf16::Routine>> {
vec![]
}
fn round_cases() -> Vec<TestCase<op::round::Routine>> {
vec![]
}
fn roundf_cases() -> Vec<TestCase<op::roundf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn roundf128_cases() -> Vec<TestCase<op::roundf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn roundevenf16_cases() -> Vec<TestCase<op::roundevenf16::Routine>> {
vec![]
}
fn roundeven_cases() -> Vec<TestCase<op::roundeven::Routine>> {
let mut v = vec![];
TestCase::append_pairs(
&mut v,
&[
// Known failure on i586
#[cfg(not(x86_no_sse))]
(
(hf64!("-0x1.e3f13ff995ffcp+38"),),
Some(hf64!("-0x1.e3f13ff994000p+38")),
),
#[cfg(x86_no_sse)]
(
(hf64!("-0x1.e3f13ff995ffcp+38"),),
Some(hf64!("-0x1.e3f13ff998000p+38")),
),
],
);
v
}
fn roundevenf_cases() -> Vec<TestCase<op::roundevenf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn roundevenf128_cases() -> Vec<TestCase<op::roundevenf128::Routine>> {
vec![]
}
fn scalbn_cases() -> Vec<TestCase<op::scalbn::Routine>> {
vec![]
}
fn scalbnf_cases() -> Vec<TestCase<op::scalbnf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn scalbnf128_cases() -> Vec<TestCase<op::scalbnf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn scalbnf16_cases() -> Vec<TestCase<op::scalbnf16::Routine>> {
vec![]
}
fn sin_cases() -> Vec<TestCase<op::sin::Routine>> {
vec![]
}
fn sincos_cases() -> Vec<TestCase<op::sincos::Routine>> {
vec![]
}
fn sincosf_cases() -> Vec<TestCase<op::sincosf::Routine>> {
vec![]
}
fn sinf_cases() -> Vec<TestCase<op::sinf::Routine>> {
vec![]
}
fn sinh_cases() -> Vec<TestCase<op::sinh::Routine>> {
vec![]
}
fn sinhf_cases() -> Vec<TestCase<op::sinhf::Routine>> {
vec![]
}
fn sqrt_cases() -> Vec<TestCase<op::sqrt::Routine>> {
vec![]
}
fn sqrtf_cases() -> Vec<TestCase<op::sqrtf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn sqrtf128_cases() -> Vec<TestCase<op::sqrtf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn sqrtf16_cases() -> Vec<TestCase<op::sqrtf16::Routine>> {
vec![]
}
fn tan_cases() -> Vec<TestCase<op::tan::Routine>> {
vec![]
}
fn tanf_cases() -> Vec<TestCase<op::tanf::Routine>> {
vec![]
}
fn tanh_cases() -> Vec<TestCase<op::tanh::Routine>> {
vec![]
}
fn tanhf_cases() -> Vec<TestCase<op::tanhf::Routine>> {
vec![]
}
fn tgamma_cases() -> Vec<TestCase<op::tgamma::Routine>> {
vec![]
}
fn tgammaf_cases() -> Vec<TestCase<op::tgammaf::Routine>> {
vec![]
}
fn trunc_cases() -> Vec<TestCase<op::trunc::Routine>> {
vec![]
}
fn truncf_cases() -> Vec<TestCase<op::truncf::Routine>> {
vec![]
}
#[cfg(f128_enabled)]
fn truncf128_cases() -> Vec<TestCase<op::truncf128::Routine>> {
vec![]
}
#[cfg(f16_enabled)]
fn truncf16_cases() -> Vec<TestCase<op::truncf16::Routine>> {
vec![]
}
fn y0_cases() -> Vec<TestCase<op::y0::Routine>> {
vec![]
}
fn y0f_cases() -> Vec<TestCase<op::y0f::Routine>> {
vec![]
}
fn y1_cases() -> Vec<TestCase<op::y1::Routine>> {
vec![]
}
fn y1f_cases() -> Vec<TestCase<op::y1f::Routine>> {
vec![]
}
fn yn_cases() -> Vec<TestCase<op::yn::Routine>> {
vec![]
}
fn ynf_cases() -> Vec<TestCase<op::ynf::Routine>> {
vec![]
}
pub trait CaseListInput: MathOp + Sized {
fn get_cases() -> Vec<TestCase<Self>>;
}
macro_rules! impl_case_list {
(
fn_name: $fn_name:ident,
attrs: [$($attr:meta),*],
) => {
paste::paste! {
$(#[$attr])*
impl CaseListInput for crate::op::$fn_name::Routine {
fn get_cases() -> Vec<TestCase<Self>> {
[< $fn_name _cases >]()
}
}
}
};
}
libm_macros::for_each_function! {
callback: impl_case_list,
}
/// This is the test generator for standalone tests, i.e. those with no basis. For this, it
/// only extracts tests with a known output.
pub fn get_test_cases_standalone<Op>(
ctx: &CheckCtx,
) -> impl Iterator<Item = (Op::RustArgs, Op::RustRet)> + use<'_, Op>
where
Op: MathOp + CaseListInput,
{
assert_eq!(ctx.basis, CheckBasis::None);
assert_eq!(ctx.gen_kind, GeneratorKind::List);
Op::get_cases()
.into_iter()
.filter_map(|x| x.output.map(|o| (x.input, o)))
}
/// Opposite of the above; extract only test cases that don't have a known output, to be run
/// against a basis.
pub fn get_test_cases_basis<Op>(
ctx: &CheckCtx,
) -> (impl Iterator<Item = Op::RustArgs> + use<'_, Op>, u64)
where
Op: MathOp + CaseListInput,
{
assert_ne!(ctx.basis, CheckBasis::None);
assert_eq!(ctx.gen_kind, GeneratorKind::List);
let cases = Op::get_cases();
let count: u64 = cases
.iter()
.filter(|case| case.output.is_none())
.count()
.try_into()
.unwrap();
(
cases
.into_iter()
.filter(|x| x.output.is_none())
.map(|x| x.input),
count,
)
}