Change to exhaustive matching for default_ulp
Make it more obvious what the expected ULP for a given routine is. This also narrows ULP to 0 for operations that require exact results.
This commit is contained in:
committed by
Trevor Gross
parent
14e6d05dfb
commit
3030182c66
@@ -4,57 +4,109 @@
|
||||
use core::f32;
|
||||
|
||||
use CheckBasis::{Mpfr, Musl};
|
||||
use Identifier as Id;
|
||||
use {BaseName as Bn, Identifier as Id};
|
||||
|
||||
use crate::{BaseName, CheckBasis, CheckCtx, Float, Identifier, Int, TestResult};
|
||||
|
||||
/// Type implementing [`IgnoreCase`].
|
||||
pub struct SpecialCase;
|
||||
|
||||
/// Default ULP allowed to differ from musl (note that musl itself may not be accurate).
|
||||
const MUSL_DEFAULT_ULP: u32 = 2;
|
||||
|
||||
/// Default ULP allowed to differ from multiprecision (i.e. infinite) results.
|
||||
const MP_DEFAULT_ULP: u32 = 1;
|
||||
|
||||
/// ULP allowed to differ from the results returned by a test basis.
|
||||
///
|
||||
/// Note that these results were obtained using 400M rounds of random inputs, which
|
||||
/// is not a value used by default.
|
||||
pub fn default_ulp(ctx: &CheckCtx) -> u32 {
|
||||
match (&ctx.basis, ctx.fn_ident) {
|
||||
// Overrides that apply to either basis
|
||||
// FMA is expected to be infinite precision.
|
||||
(_, Id::Fma | Id::Fmaf) => 0,
|
||||
(_, Id::J0 | Id::J0f | Id::J1 | Id::J1f | Id::Y0 | Id::Y0f | Id::Y1 | Id::Y1f) => 800_000,
|
||||
(_, Id::Jn | Id::Jnf | Id::Yn | Id::Ynf) => 1000,
|
||||
(_, Id::Erfc | Id::Erfcf) => 4,
|
||||
// ULP compared to the infinite (MPFR) result.
|
||||
let mut ulp = match ctx.base_name {
|
||||
// Operations that require exact results. This list should correlate with what we
|
||||
// have documented at <https://doc.rust-lang.org/std/primitive.f32.html>.
|
||||
Bn::Ceil
|
||||
| Bn::Copysign
|
||||
| Bn::Fabs
|
||||
| Bn::Fdim
|
||||
| Bn::Floor
|
||||
| Bn::Fma
|
||||
| Bn::Fmax
|
||||
| Bn::Fmin
|
||||
| Bn::Fmod
|
||||
| Bn::Frexp
|
||||
| Bn::Ldexp
|
||||
| Bn::Modf
|
||||
| Bn::Nextafter
|
||||
| Bn::Remainder
|
||||
| Bn::Remquo
|
||||
| Bn::Rint
|
||||
| Bn::Round
|
||||
| Bn::Scalbn
|
||||
| Bn::Sqrt
|
||||
| Bn::Trunc => 0,
|
||||
|
||||
// Overrides for musl
|
||||
#[cfg(x86_no_sse)]
|
||||
(Musl, Id::Asinh | Id::Asinhf) => 6,
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
(Musl, Id::Exp10 | Id::Exp10f) => 4,
|
||||
(Musl, Id::Lgamma | Id::LgammaR | Id::Lgammaf | Id::LgammafR) => 400,
|
||||
(Musl, Id::Sincosf) => 500,
|
||||
(Musl, Id::Tanh | Id::Tanhf) => 4,
|
||||
(Musl, Id::Tgamma) => 20,
|
||||
// Operations that aren't required to be exact, but our implementations are.
|
||||
Bn::Cbrt if ctx.fn_ident != Id::Cbrt => 0,
|
||||
Bn::Ilogb => 0,
|
||||
Bn::Tgamma if ctx.fn_ident != Id::Tgamma => 0,
|
||||
|
||||
// Overrides for MPFR
|
||||
(Mpfr, Id::Acosh) => 4,
|
||||
(Mpfr, Id::Acoshf) => 4,
|
||||
(Mpfr, Id::Asinh | Id::Asinhf) => 2,
|
||||
(Mpfr, Id::Atanh | Id::Atanhf) => 2,
|
||||
(Mpfr, Id::Exp10 | Id::Exp10f) => 6,
|
||||
(Mpfr, Id::Lgamma | Id::LgammaR | Id::Lgammaf | Id::LgammafR) => 16,
|
||||
(Mpfr, Id::Sinh | Id::Sinhf) => 2,
|
||||
(Mpfr, Id::Tanh | Id::Tanhf) => 2,
|
||||
(Mpfr, Id::Tgamma) => 20,
|
||||
// Bessel functions have large inaccuracies.
|
||||
Bn::J0 | Bn::J1 | Bn::Y0 | Bn::Y1 => 8_000_000,
|
||||
Bn::Jn | Bn::Yn => 1_000,
|
||||
|
||||
// Defaults
|
||||
(Musl, _) => MUSL_DEFAULT_ULP,
|
||||
(Mpfr, _) => MP_DEFAULT_ULP,
|
||||
// For all other operations, specify our implementation's worst case precision.
|
||||
Bn::Acos => 1,
|
||||
Bn::Acosh => 4,
|
||||
Bn::Asin => 1,
|
||||
Bn::Asinh => 2,
|
||||
Bn::Atan => 1,
|
||||
Bn::Atan2 => 1,
|
||||
Bn::Atanh => 2,
|
||||
Bn::Cbrt => 1,
|
||||
Bn::Cos => 1,
|
||||
Bn::Cosh => 1,
|
||||
Bn::Erf => 1,
|
||||
Bn::Erfc => 4,
|
||||
Bn::Exp => 1,
|
||||
Bn::Exp10 => 6,
|
||||
Bn::Exp2 => 1,
|
||||
Bn::Expm1 => 1,
|
||||
Bn::Hypot => 1,
|
||||
Bn::Lgamma | Bn::LgammaR => 16,
|
||||
Bn::Log => 1,
|
||||
Bn::Log10 => 1,
|
||||
Bn::Log1p => 1,
|
||||
Bn::Log2 => 1,
|
||||
Bn::Pow => 1,
|
||||
Bn::Sin => 1,
|
||||
Bn::Sincos => 1,
|
||||
Bn::Sinh => 2,
|
||||
Bn::Tan => 1,
|
||||
Bn::Tanh => 2,
|
||||
Bn::Tgamma => 20,
|
||||
};
|
||||
|
||||
// There are some cases where musl's approximation is less accurate than ours. For these
|
||||
// cases, increase the ULP.
|
||||
if ctx.basis == Musl {
|
||||
match ctx.base_name {
|
||||
Bn::Cosh => ulp = 2,
|
||||
Bn::Exp10 if usize::BITS < 64 => ulp = 4,
|
||||
Bn::Lgamma | Bn::LgammaR => ulp = 400,
|
||||
Bn::Tanh => ulp = 4,
|
||||
_ if ctx.fn_ident == Id::Sincosf => ulp = 500,
|
||||
_ if ctx.fn_ident == Id::Tgamma => ulp = 20,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// In some cases, our implementation is less accurate than musl on i586.
|
||||
if cfg!(x86_no_sse) {
|
||||
match ctx.fn_ident {
|
||||
Id::Log1p | Id::Log1pf => ulp = 2,
|
||||
Id::Round => ulp = 1,
|
||||
Id::Tan => ulp = 2,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
ulp
|
||||
}
|
||||
|
||||
/// Don't run further validation on this test case.
|
||||
|
||||
Reference in New Issue
Block a user