Change the CheckCtx constructor to take a Name enum
This prepares to eliminate some reliance on string matching but does not yet make those changes.
This commit is contained in:
@@ -628,9 +628,9 @@ impl VisitMut for MacroReplace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the unsuffixed name of a function.
|
/// Return the unsuffixed version of a function name; e.g. `abs` and `absf` both return `abs`,
|
||||||
|
/// `lgamma_r` and `lgammaf_r` both return `lgamma_r`.
|
||||||
fn base_name(name: &str) -> &str {
|
fn base_name(name: &str) -> &str {
|
||||||
// Keep this in sync with `libm_test::base_name`
|
|
||||||
let known_mappings = &[
|
let known_mappings = &[
|
||||||
("erff", "erf"),
|
("erff", "erf"),
|
||||||
("erf", "erf"),
|
("erf", "erf"),
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ where
|
|||||||
let name = Op::NAME_STR;
|
let name = Op::NAME_STR;
|
||||||
|
|
||||||
let ulp = libm_test::musl_allowed_ulp(name);
|
let ulp = libm_test::musl_allowed_ulp(name);
|
||||||
let ctx = CheckCtx::new(ulp, name, CheckBasis::Musl);
|
let ctx = CheckCtx::new(ulp, Op::NAME, CheckBasis::Musl);
|
||||||
let benchvec: Vec<_> =
|
let benchvec: Vec<_> =
|
||||||
random::get_test_cases::<Op::RustArgs>(&ctx).take(BENCH_ITER_ITEMS).collect();
|
random::get_test_cases::<Op::RustArgs>(&ctx).take(BENCH_ITER_ITEMS).collect();
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,10 @@ pub fn get_test_cases<RustArgs>(ctx: &CheckCtx) -> impl Iterator<Item = RustArgs
|
|||||||
where
|
where
|
||||||
CachedInput: GenerateInput<RustArgs>,
|
CachedInput: GenerateInput<RustArgs>,
|
||||||
{
|
{
|
||||||
let inputs =
|
let inputs = if ctx.fn_name_str == "jn" || ctx.fn_name_str == "jnf" {
|
||||||
if ctx.fn_name == "jn" || ctx.fn_name == "jnf" { &TEST_CASES_JN } else { &TEST_CASES };
|
&TEST_CASES_JN
|
||||||
|
} else {
|
||||||
|
&TEST_CASES
|
||||||
|
};
|
||||||
inputs.get_cases()
|
inputs.get_cases()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,27 +17,6 @@ pub type TestResult<T = (), E = anyhow::Error> = Result<T, E>;
|
|||||||
// List of all files present in libm's source
|
// List of all files present in libm's source
|
||||||
include!(concat!(env!("OUT_DIR"), "/all_files.rs"));
|
include!(concat!(env!("OUT_DIR"), "/all_files.rs"));
|
||||||
|
|
||||||
/// Return the unsuffixed version of a function name; e.g. `abs` and `absf` both return `abs`,
|
|
||||||
/// `lgamma_r` and `lgammaf_r` both return `lgamma_r`.
|
|
||||||
pub fn base_name(name: &str) -> &str {
|
|
||||||
let known_mappings = &[
|
|
||||||
("erff", "erf"),
|
|
||||||
("erf", "erf"),
|
|
||||||
("lgammaf_r", "lgamma_r"),
|
|
||||||
("modff", "modf"),
|
|
||||||
("modf", "modf"),
|
|
||||||
];
|
|
||||||
|
|
||||||
match known_mappings.iter().find(|known| known.0 == name) {
|
|
||||||
Some(found) => found.1,
|
|
||||||
None => name
|
|
||||||
.strip_suffix("f")
|
|
||||||
.or_else(|| name.strip_suffix("f16"))
|
|
||||||
.or_else(|| name.strip_suffix("f128"))
|
|
||||||
.unwrap_or(name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// True if `EMULATED` is set and nonempty. Used to determine how many iterations to run.
|
/// True if `EMULATED` is set and nonempty. Used to determine how many iterations to run.
|
||||||
pub const fn emulated() -> bool {
|
pub const fn emulated() -> bool {
|
||||||
match option_env!("EMULATED") {
|
match option_env!("EMULATED") {
|
||||||
|
|||||||
@@ -111,25 +111,25 @@ impl MaybeOverride<(f32,)> for SpecialCase {
|
|||||||
ctx: &CheckCtx,
|
ctx: &CheckCtx,
|
||||||
) -> Option<TestResult> {
|
) -> Option<TestResult> {
|
||||||
if ctx.basis == CheckBasis::Musl {
|
if ctx.basis == CheckBasis::Musl {
|
||||||
if ctx.fn_name == "expm1f" && input.0 > 80.0 && actual.is_infinite() {
|
if ctx.fn_name_str == "expm1f" && input.0 > 80.0 && actual.is_infinite() {
|
||||||
// we return infinity but the number is representable
|
// we return infinity but the number is representable
|
||||||
return XFAIL;
|
return XFAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.fn_name == "sinhf" && input.0.abs() > 80.0 && actual.is_nan() {
|
if ctx.fn_name_str == "sinhf" && input.0.abs() > 80.0 && actual.is_nan() {
|
||||||
// we return some NaN that should be real values or infinite
|
// we return some NaN that should be real values or infinite
|
||||||
// doesn't seem to happen on x86
|
// doesn't seem to happen on x86
|
||||||
return XFAIL;
|
return XFAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.fn_name == "acoshf" && input.0 < -1.0 {
|
if ctx.fn_name_str == "acoshf" && input.0 < -1.0 {
|
||||||
// acoshf is undefined for x <= 1.0, but we return a random result at lower
|
// acoshf is undefined for x <= 1.0, but we return a random result at lower
|
||||||
// values.
|
// values.
|
||||||
return XFAIL;
|
return XFAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.fn_name == "lgammaf" || ctx.fn_name == "lgammaf_r" && input.0 < 0.0 {
|
if ctx.fn_name_str == "lgammaf" || ctx.fn_name_str == "lgammaf_r" && input.0 < 0.0 {
|
||||||
// loggamma should not be defined for x < 0, yet we both return results
|
// loggamma should not be defined for x < 0, yet we both return results
|
||||||
return XFAIL;
|
return XFAIL;
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ impl MaybeOverride<(f32,)> for SpecialCase {
|
|||||||
// On MPFR for lgammaf_r, we set -1 as the integer result for negative infinity but MPFR
|
// On MPFR for lgammaf_r, we set -1 as the integer result for negative infinity but MPFR
|
||||||
// sets +1
|
// sets +1
|
||||||
if ctx.basis == CheckBasis::Mpfr
|
if ctx.basis == CheckBasis::Mpfr
|
||||||
&& ctx.fn_name == "lgammaf_r"
|
&& ctx.fn_name_str == "lgammaf_r"
|
||||||
&& input.0 == f32::NEG_INFINITY
|
&& input.0 == f32::NEG_INFINITY
|
||||||
&& actual.abs() == expected.abs()
|
&& actual.abs() == expected.abs()
|
||||||
{
|
{
|
||||||
@@ -166,13 +166,13 @@ impl MaybeOverride<(f64,)> for SpecialCase {
|
|||||||
ctx: &CheckCtx,
|
ctx: &CheckCtx,
|
||||||
) -> Option<TestResult> {
|
) -> Option<TestResult> {
|
||||||
if ctx.basis == CheckBasis::Musl {
|
if ctx.basis == CheckBasis::Musl {
|
||||||
if cfg!(target_arch = "x86") && ctx.fn_name == "acosh" && input.0 < 1.0 {
|
if cfg!(target_arch = "x86") && ctx.fn_name_str == "acosh" && input.0 < 1.0 {
|
||||||
// The function is undefined, both implementations return random results
|
// The function is undefined, both implementations return random results
|
||||||
return SKIP;
|
return SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(x86_no_sse)
|
if cfg!(x86_no_sse)
|
||||||
&& ctx.fn_name == "ceil"
|
&& ctx.fn_name_str == "ceil"
|
||||||
&& input.0 < 0.0
|
&& input.0 < 0.0
|
||||||
&& input.0 > -1.0
|
&& input.0 > -1.0
|
||||||
&& expected == F::ZERO
|
&& expected == F::ZERO
|
||||||
@@ -183,13 +183,13 @@ impl MaybeOverride<(f64,)> for SpecialCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.fn_name == "acosh" && input.0 < 1.0 {
|
if ctx.fn_name_str == "acosh" && input.0 < 1.0 {
|
||||||
// The function is undefined for the inputs, musl and our libm both return
|
// The function is undefined for the inputs, musl and our libm both return
|
||||||
// random results.
|
// random results.
|
||||||
return XFAIL;
|
return XFAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.fn_name == "lgamma" || ctx.fn_name == "lgamma_r" && input.0 < 0.0 {
|
if ctx.fn_name_str == "lgamma" || ctx.fn_name_str == "lgamma_r" && input.0 < 0.0 {
|
||||||
// loggamma should not be defined for x < 0, yet we both return results
|
// loggamma should not be defined for x < 0, yet we both return results
|
||||||
return XFAIL;
|
return XFAIL;
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ impl MaybeOverride<(f64,)> for SpecialCase {
|
|||||||
// On MPFR for lgamma_r, we set -1 as the integer result for negative infinity but MPFR
|
// On MPFR for lgamma_r, we set -1 as the integer result for negative infinity but MPFR
|
||||||
// sets +1
|
// sets +1
|
||||||
if ctx.basis == CheckBasis::Mpfr
|
if ctx.basis == CheckBasis::Mpfr
|
||||||
&& ctx.fn_name == "lgamma_r"
|
&& ctx.fn_name_str == "lgamma_r"
|
||||||
&& input.0 == f64::NEG_INFINITY
|
&& input.0 == f64::NEG_INFINITY
|
||||||
&& actual.abs() == expected.abs()
|
&& actual.abs() == expected.abs()
|
||||||
{
|
{
|
||||||
@@ -219,7 +219,7 @@ impl MaybeOverride<(f64,)> for SpecialCase {
|
|||||||
|
|
||||||
/// Check NaN bits if the function requires it
|
/// Check NaN bits if the function requires it
|
||||||
fn maybe_check_nan_bits<F: Float>(actual: F, expected: F, ctx: &CheckCtx) -> Option<TestResult> {
|
fn maybe_check_nan_bits<F: Float>(actual: F, expected: F, ctx: &CheckCtx) -> Option<TestResult> {
|
||||||
if !(ctx.base_name == "fabs" || ctx.base_name == "copysign") {
|
if !(ctx.base_name_str == "fabs" || ctx.base_name_str == "copysign") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ fn maybe_skip_binop_nan<F1: Float, F2: Float>(
|
|||||||
) -> Option<TestResult> {
|
) -> Option<TestResult> {
|
||||||
match ctx.basis {
|
match ctx.basis {
|
||||||
CheckBasis::Musl => {
|
CheckBasis::Musl => {
|
||||||
if (ctx.base_name == "fmax" || ctx.base_name == "fmin")
|
if (ctx.base_name_str == "fmax" || ctx.base_name_str == "fmin")
|
||||||
&& (input.0.is_nan() || input.1.is_nan())
|
&& (input.0.is_nan() || input.1.is_nan())
|
||||||
&& expected.is_nan()
|
&& expected.is_nan()
|
||||||
{
|
{
|
||||||
@@ -287,7 +287,7 @@ fn maybe_skip_binop_nan<F1: Float, F2: Float>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckBasis::Mpfr => {
|
CheckBasis::Mpfr => {
|
||||||
if ctx.base_name == "copysign" && input.1.is_nan() {
|
if ctx.base_name_str == "copysign" && input.1.is_nan() {
|
||||||
SKIP
|
SKIP
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -308,7 +308,7 @@ impl MaybeOverride<(i32, f32)> for SpecialCase {
|
|||||||
CheckBasis::Musl => bessel_prec_dropoff(input, ulp, ctx),
|
CheckBasis::Musl => bessel_prec_dropoff(input, ulp, ctx),
|
||||||
CheckBasis::Mpfr => {
|
CheckBasis::Mpfr => {
|
||||||
// We return +0.0, MPFR returns -0.0
|
// We return +0.0, MPFR returns -0.0
|
||||||
if ctx.fn_name == "jnf"
|
if ctx.fn_name_str == "jnf"
|
||||||
&& input.1 == f32::NEG_INFINITY
|
&& input.1 == f32::NEG_INFINITY
|
||||||
&& actual == F::ZERO
|
&& actual == F::ZERO
|
||||||
&& expected == F::ZERO
|
&& expected == F::ZERO
|
||||||
@@ -333,7 +333,7 @@ impl MaybeOverride<(i32, f64)> for SpecialCase {
|
|||||||
CheckBasis::Musl => bessel_prec_dropoff(input, ulp, ctx),
|
CheckBasis::Musl => bessel_prec_dropoff(input, ulp, ctx),
|
||||||
CheckBasis::Mpfr => {
|
CheckBasis::Mpfr => {
|
||||||
// We return +0.0, MPFR returns -0.0
|
// We return +0.0, MPFR returns -0.0
|
||||||
if ctx.fn_name == "jn"
|
if ctx.fn_name_str == "jn"
|
||||||
&& input.1 == f64::NEG_INFINITY
|
&& input.1 == f64::NEG_INFINITY
|
||||||
&& actual == F::ZERO
|
&& actual == F::ZERO
|
||||||
&& expected == F::ZERO
|
&& expected == F::ZERO
|
||||||
@@ -353,7 +353,7 @@ fn bessel_prec_dropoff<F: Float>(
|
|||||||
ulp: &mut u32,
|
ulp: &mut u32,
|
||||||
ctx: &CheckCtx,
|
ctx: &CheckCtx,
|
||||||
) -> Option<TestResult> {
|
) -> Option<TestResult> {
|
||||||
if ctx.base_name == "jn" {
|
if ctx.base_name_str == "jn" {
|
||||||
if input.0 > 4000 {
|
if input.0 > 4000 {
|
||||||
return XFAIL;
|
return XFAIL;
|
||||||
} else if input.0 > 2000 {
|
} else if input.0 > 2000 {
|
||||||
|
|||||||
@@ -11,25 +11,33 @@ use std::fmt;
|
|||||||
|
|
||||||
use anyhow::{Context, bail, ensure};
|
use anyhow::{Context, bail, ensure};
|
||||||
|
|
||||||
use crate::{Float, Int, MaybeOverride, SpecialCase, TestResult};
|
use crate::{BaseName, Float, Int, MaybeOverride, Name, SpecialCase, TestResult};
|
||||||
|
|
||||||
/// Context passed to [`CheckOutput`].
|
/// Context passed to [`CheckOutput`].
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct CheckCtx {
|
pub struct CheckCtx {
|
||||||
/// Allowed ULP deviation
|
/// Allowed ULP deviation
|
||||||
pub ulp: u32,
|
pub ulp: u32,
|
||||||
|
pub fn_name: Name,
|
||||||
|
pub base_name: BaseName,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub fn_name: &'static str,
|
pub fn_name_str: &'static str,
|
||||||
/// Return the unsuffixed version of the function name.
|
/// Return the unsuffixed version of the function name.
|
||||||
pub base_name: &'static str,
|
pub base_name_str: &'static str,
|
||||||
/// Source of truth for tests.
|
/// Source of truth for tests.
|
||||||
pub basis: CheckBasis,
|
pub basis: CheckBasis,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckCtx {
|
impl CheckCtx {
|
||||||
pub fn new(ulp: u32, fname: &'static str, basis: CheckBasis) -> Self {
|
pub fn new(ulp: u32, fn_name: Name, basis: CheckBasis) -> Self {
|
||||||
let base_name = crate::base_name(fname);
|
Self {
|
||||||
Self { ulp, fn_name: fname, base_name, basis }
|
ulp,
|
||||||
|
fn_name,
|
||||||
|
fn_name_str: fn_name.as_str(),
|
||||||
|
base_name: fn_name.base_name(),
|
||||||
|
base_name_str: fn_name.base_name().as_str(),
|
||||||
|
basis,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ where
|
|||||||
{
|
{
|
||||||
let name = Op::NAME_STR;
|
let name = Op::NAME_STR;
|
||||||
let ulp = musl_allowed_ulp(name);
|
let ulp = musl_allowed_ulp(name);
|
||||||
let ctx = CheckCtx::new(ulp, name, CheckBasis::Musl);
|
let ctx = CheckCtx::new(ulp, Op::NAME, CheckBasis::Musl);
|
||||||
let cases = random::get_test_cases::<Op::RustArgs>(&ctx);
|
let cases = random::get_test_cases::<Op::RustArgs>(&ctx);
|
||||||
|
|
||||||
for input in cases {
|
for input in cases {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ where
|
|||||||
|
|
||||||
let ulp = multiprec_allowed_ulp(name);
|
let ulp = multiprec_allowed_ulp(name);
|
||||||
let mut mp_vals = Op::new_mp();
|
let mut mp_vals = Op::new_mp();
|
||||||
let ctx = CheckCtx::new(ulp, name, CheckBasis::Mpfr);
|
let ctx = CheckCtx::new(ulp, Op::NAME, CheckBasis::Mpfr);
|
||||||
let cases = random::get_test_cases::<Op::RustArgs>(&ctx);
|
let cases = random::get_test_cases::<Op::RustArgs>(&ctx);
|
||||||
|
|
||||||
for input in cases {
|
for input in cases {
|
||||||
|
|||||||
Reference in New Issue
Block a user