From 7b05fd482350fdae9ed66e4a9731bcb1a5e146c7 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 11 Feb 2025 02:17:06 +0000 Subject: [PATCH 1/2] Fix parsing of negative hex float literals in util --- crates/util/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/util/src/main.rs b/crates/util/src/main.rs index a519713c0..710adbb17 100644 --- a/crates/util/src/main.rs +++ b/crates/util/src/main.rs @@ -274,7 +274,7 @@ fn parse(input: &[&str], idx: usize) -> T { let msg = || format!("invalid {} input '{s}'", type_name::()); - if s.starts_with("0x") { + if s.starts_with("0x") || s.starts_with("-0x") { return T::from_str_radix(s, 16).unwrap_or_else(|_| panic!("{}", msg())); } From 27abf04be67c50825fbac6136d9b9b9bd1988453 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 11 Feb 2025 00:17:32 +0000 Subject: [PATCH 2/2] Add `roundeven{,f,f16,f128}` C23 specifies a new set of `roundeven` functions that round to the nearest integral, with ties to even. It does not raise any floating point exceptions. This behavior is similar to two other functions: 1. `rint`, which rounds to the nearest integer respecting rounding mode and possibly raising exceptions. 2. `nearbyint`, which is identical to `rint` except it may not raise exceptions. Technically `rint`, `nearbyint`, and `roundeven` all behave the same in Rust because we assume default floating point environment. The backends are allowed to lower to `roundeven`, however, so we should provide it in case the fallback is needed. Add the `roundeven` family here and convert `rint` to a function that takes a rounding mode. This currently has no effect. --- crates/libm-macros/src/shared.rs | 104 ++++++++++++++++-- crates/libm-test/benches/icount.rs | 4 + crates/libm-test/benches/random.rs | 4 + crates/libm-test/src/domain.rs | 1 + crates/libm-test/src/gen/case_list.rs | 39 ++++++- crates/libm-test/src/mpfloat.rs | 8 ++ crates/libm-test/src/precision.rs | 3 +- crates/libm-test/tests/compare_built_musl.rs | 4 + crates/util/src/main.rs | 4 + etc/function-definitions.json | 34 ++++-- etc/function-list.txt | 4 + src/libm_helper.rs | 42 ++++---- src/math/generic/mod.rs | 2 +- src/math/generic/rint.rs | 105 ++++++++++++++----- src/math/mod.rs | 14 +-- src/math/rint.rs | 33 +++++- src/math/rintf.rs | 14 --- src/math/rintf128.rs | 5 - src/math/rintf16.rs | 5 - src/math/roundeven.rs | 35 +++++++ 20 files changed, 363 insertions(+), 101 deletions(-) delete mode 100644 src/math/rintf.rs delete mode 100644 src/math/rintf128.rs delete mode 100644 src/math/rintf16.rs create mode 100644 src/math/roundeven.rs diff --git a/crates/libm-macros/src/shared.rs b/crates/libm-macros/src/shared.rs index cb5a1d187..5e58220eb 100644 --- a/crates/libm-macros/src/shared.rs +++ b/crates/libm-macros/src/shared.rs @@ -9,7 +9,16 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] FloatTy::F16, Signature { args: &[Ty::F16], returns: &[Ty::F16] }, None, - &["ceilf16", "fabsf16", "floorf16", "rintf16", "roundf16", "sqrtf16", "truncf16"], + &[ + "ceilf16", + "fabsf16", + "floorf16", + "rintf16", + "roundevenf16", + "roundf16", + "sqrtf16", + "truncf16", + ], ), ( // `fn(f32) -> f32` @@ -17,10 +26,43 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] Signature { args: &[Ty::F32], returns: &[Ty::F32] }, None, &[ - "acosf", "acoshf", "asinf", "asinhf", "atanf", "atanhf", "cbrtf", "ceilf", "cosf", - "coshf", "erfcf", "erff", "exp10f", "exp2f", "expf", "expm1f", "fabsf", "floorf", - "j0f", "j1f", "lgammaf", "log10f", "log1pf", "log2f", "logf", "rintf", "roundf", - "sinf", "sinhf", "sqrtf", "tanf", "tanhf", "tgammaf", "truncf", "y0f", "y1f", + "acosf", + "acoshf", + "asinf", + "asinhf", + "atanf", + "atanhf", + "cbrtf", + "ceilf", + "cosf", + "coshf", + "erfcf", + "erff", + "exp10f", + "exp2f", + "expf", + "expm1f", + "fabsf", + "floorf", + "j0f", + "j1f", + "lgammaf", + "log10f", + "log1pf", + "log2f", + "logf", + "rintf", + "roundevenf", + "roundf", + "sinf", + "sinhf", + "sqrtf", + "tanf", + "tanhf", + "tgammaf", + "truncf", + "y0f", + "y1f", ], ), ( @@ -29,10 +71,43 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] Signature { args: &[Ty::F64], returns: &[Ty::F64] }, None, &[ - "acos", "acosh", "asin", "asinh", "atan", "atanh", "cbrt", "ceil", "cos", "cosh", - "erf", "erfc", "exp", "exp10", "exp2", "expm1", "fabs", "floor", "j0", "j1", "lgamma", - "log", "log10", "log1p", "log2", "rint", "round", "sin", "sinh", "sqrt", "tan", "tanh", - "tgamma", "trunc", "y0", "y1", + "acos", + "acosh", + "asin", + "asinh", + "atan", + "atanh", + "cbrt", + "ceil", + "cos", + "cosh", + "erf", + "erfc", + "exp", + "exp10", + "exp2", + "expm1", + "fabs", + "floor", + "j0", + "j1", + "lgamma", + "log", + "log10", + "log1p", + "log2", + "rint", + "round", + "roundeven", + "sin", + "sinh", + "sqrt", + "tan", + "tanh", + "tgamma", + "trunc", + "y0", + "y1", ], ), ( @@ -40,7 +115,16 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] FloatTy::F128, Signature { args: &[Ty::F128], returns: &[Ty::F128] }, None, - &["ceilf128", "fabsf128", "floorf128", "rintf128", "roundf128", "sqrtf128", "truncf128"], + &[ + "ceilf128", + "fabsf128", + "floorf128", + "rintf128", + "roundevenf128", + "roundf128", + "sqrtf128", + "truncf128", + ], ), ( // `(f16, f16) -> f16` diff --git a/crates/libm-test/benches/icount.rs b/crates/libm-test/benches/icount.rs index e28f4973c..4a10ec383 100644 --- a/crates/libm-test/benches/icount.rs +++ b/crates/libm-test/benches/icount.rs @@ -274,6 +274,10 @@ main!( icount_bench_rintf16_group, icount_bench_rintf_group, icount_bench_round_group, + icount_bench_roundeven_group, + icount_bench_roundevenf128_group, + icount_bench_roundevenf16_group, + icount_bench_roundevenf_group, icount_bench_roundf128_group, icount_bench_roundf16_group, icount_bench_roundf_group, diff --git a/crates/libm-test/benches/random.rs b/crates/libm-test/benches/random.rs index 6f6b05d95..17e4e0d55 100644 --- a/crates/libm-test/benches/random.rs +++ b/crates/libm-test/benches/random.rs @@ -154,6 +154,10 @@ libm_macros::for_each_function! { | ldexpf16 | rintf128 | rintf16 + | roundeven + | roundevenf + | roundevenf128 + | roundevenf16 | roundf128 | roundf16 | scalbnf128 diff --git a/crates/libm-test/src/domain.rs b/crates/libm-test/src/domain.rs index c662e95b4..41e948461 100644 --- a/crates/libm-test/src/domain.rs +++ b/crates/libm-test/src/domain.rs @@ -246,6 +246,7 @@ pub fn get_domain( BaseName::Remquo => &EitherPrim::UNBOUNDED2[..], BaseName::Rint => &EitherPrim::UNBOUNDED1[..], BaseName::Round => &EitherPrim::UNBOUNDED1[..], + BaseName::Roundeven => &EitherPrim::UNBOUNDED1[..], BaseName::Scalbn => &EitherPrim::UNBOUNDED_F_I[..], BaseName::Sin => &EitherPrim::TRIG[..], BaseName::Sincos => &EitherPrim::TRIG[..], diff --git a/crates/libm-test/src/gen/case_list.rs b/crates/libm-test/src/gen/case_list.rs index 49e731b88..8c7a735fa 100644 --- a/crates/libm-test/src/gen/case_list.rs +++ b/crates/libm-test/src/gen/case_list.rs @@ -6,6 +6,7 @@ //! //! This is useful for adding regression tests or expected failures. +use libm::hf64; #[cfg(f128_enabled)] use libm::hf128; @@ -574,7 +575,15 @@ fn remquof_cases() -> Vec> { } fn rint_cases() -> Vec> { - vec![] + let mut v = vec![]; + TestCase::append_pairs( + &mut v, + &[ + // Failure on i586 + ((hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff994000p+38"))), + ], + ); + v } fn rintf_cases() -> Vec> { @@ -591,6 +600,11 @@ fn rintf16_cases() -> Vec> { vec![] } +#[cfg(f16_enabled)] +fn roundf16_cases() -> Vec> { + vec![] +} + fn round_cases() -> Vec> { vec![] } @@ -605,7 +619,28 @@ fn roundf128_cases() -> Vec> { } #[cfg(f16_enabled)] -fn roundf16_cases() -> Vec> { +fn roundevenf16_cases() -> Vec> { + vec![] +} + +fn roundeven_cases() -> Vec> { + let mut v = vec![]; + TestCase::append_pairs( + &mut v, + &[ + // Failure on i586 + ((hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff994000p+38"))), + ], + ); + v +} + +fn roundevenf_cases() -> Vec> { + vec![] +} + +#[cfg(f128_enabled)] +fn roundevenf128_cases() -> Vec> { vec![] } diff --git a/crates/libm-test/src/mpfloat.rs b/crates/libm-test/src/mpfloat.rs index 63cdebe4e..9b51dc605 100644 --- a/crates/libm-test/src/mpfloat.rs +++ b/crates/libm-test/src/mpfloat.rs @@ -184,6 +184,10 @@ libm_macros::for_each_function! { rintf128, rintf16, round, + roundeven, + roundevenf, + roundevenf128, + roundevenf16, roundf, roundf128, roundf16, @@ -253,6 +257,8 @@ impl_no_round! { rint => round_even_mut; // FIXME: respect rounding mode rintf => round_even_mut; // FIXME: respect rounding mode round => round_mut; + roundeven => round_even_mut; + roundevenf => round_even_mut; roundf => round_mut; trunc => trunc_mut; truncf => trunc_mut; @@ -265,6 +271,7 @@ impl_no_round! { floorf16 => floor_mut; rintf16 => round_even_mut; // FIXME: respect rounding mode roundf16 => round_mut; + roundevenf16 => round_even_mut; truncf16 => trunc_mut; } @@ -275,6 +282,7 @@ impl_no_round! { floorf128 => floor_mut; rintf128 => round_even_mut; // FIXME: respect rounding mode roundf128 => round_mut; + roundevenf128 => round_even_mut; truncf128 => trunc_mut; } diff --git a/crates/libm-test/src/precision.rs b/crates/libm-test/src/precision.rs index 8b0892546..8916b43ab 100644 --- a/crates/libm-test/src/precision.rs +++ b/crates/libm-test/src/precision.rs @@ -40,6 +40,7 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { | Bn::Remquo | Bn::Rint | Bn::Round + | Bn::Roundeven | Bn::Scalbn | Bn::Sqrt | Bn::Trunc => 0, @@ -282,7 +283,7 @@ impl MaybeOverride<(f64,)> for SpecialCase { } if cfg!(x86_no_sse) - && ctx.base_name == BaseName::Rint + && (ctx.base_name == BaseName::Rint || ctx.base_name == BaseName::Roundeven) && (expected - actual).abs() <= F::ONE && (expected - actual).abs() > F::ZERO { diff --git a/crates/libm-test/tests/compare_built_musl.rs b/crates/libm-test/tests/compare_built_musl.rs index ffd7f1f60..2b16b9aa0 100644 --- a/crates/libm-test/tests/compare_built_musl.rs +++ b/crates/libm-test/tests/compare_built_musl.rs @@ -126,6 +126,10 @@ libm_macros::for_each_function! { ldexpf16, rintf128, rintf16, + roundeven, + roundevenf, + roundevenf128, + roundevenf16, roundf128, roundf16, scalbnf128, diff --git a/crates/util/src/main.rs b/crates/util/src/main.rs index 710adbb17..130ac4531 100644 --- a/crates/util/src/main.rs +++ b/crates/util/src/main.rs @@ -123,6 +123,10 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) { | ldexpf16 | rintf128 | rintf16 + | roundeven + | roundevenf + | roundevenf128 + | roundevenf16 | roundf128 | roundf16 | scalbnf128 diff --git a/etc/function-definitions.json b/etc/function-definitions.json index 008a47df2..a47aaad57 100644 --- a/etc/function-definitions.json +++ b/etc/function-definitions.json @@ -791,7 +791,6 @@ "sources": [ "src/math/arch/aarch64.rs", "src/math/arch/wasm32.rs", - "src/math/generic/rint.rs", "src/math/rint.rs" ], "type": "f64" @@ -800,22 +799,19 @@ "sources": [ "src/math/arch/aarch64.rs", "src/math/arch/wasm32.rs", - "src/math/generic/rint.rs", - "src/math/rintf.rs" + "src/math/rint.rs" ], "type": "f32" }, "rintf128": { "sources": [ - "src/math/generic/rint.rs", - "src/math/rintf128.rs" + "src/math/rint.rs" ], "type": "f128" }, "rintf16": { "sources": [ - "src/math/generic/rint.rs", - "src/math/rintf16.rs" + "src/math/rint.rs" ], "type": "f16" }, @@ -826,6 +822,30 @@ ], "type": "f64" }, + "roundeven": { + "sources": [ + "src/math/roundeven.rs" + ], + "type": "f64" + }, + "roundevenf": { + "sources": [ + "src/math/roundeven.rs" + ], + "type": "f32" + }, + "roundevenf128": { + "sources": [ + "src/math/roundeven.rs" + ], + "type": "f128" + }, + "roundevenf16": { + "sources": [ + "src/math/roundeven.rs" + ], + "type": "f16" + }, "roundf": { "sources": [ "src/math/generic/round.rs", diff --git a/etc/function-list.txt b/etc/function-list.txt index 90ca8f34e..1f226c8c0 100644 --- a/etc/function-list.txt +++ b/etc/function-list.txt @@ -125,6 +125,10 @@ rintf rintf128 rintf16 round +roundeven +roundevenf +roundevenf128 +roundevenf16 roundf roundf128 roundf16 diff --git a/src/libm_helper.rs b/src/libm_helper.rs index 489dbc0d4..dfa1ff77b 100644 --- a/src/libm_helper.rs +++ b/src/libm_helper.rs @@ -94,6 +94,7 @@ libm_helper! { (fn remquo(x: f32, y: f32) -> (f32, i32); => remquof); (fn rint(x: f32) -> (f32); => rintf); (fn round(x: f32) -> (f32); => roundf); + (fn roundeven(x: f32) -> (f32); => roundevenf); (fn scalbn(x: f32, n: i32) -> (f32); => scalbnf); (fn sin(x: f32) -> (f32); => sinf); (fn sincos(x: f32) -> (f32, f32); => sincosf); @@ -167,6 +168,7 @@ libm_helper! { (fn remquo(x: f64, y: f64) -> (f64, i32); => remquo); (fn rint(x: f64) -> (f64); => rint); (fn round(x: f64) -> (f64); => round); + (fn roundevem(x: f64) -> (f64); => roundeven); (fn scalbn(x: f64, n: i32) -> (f64); => scalbn); (fn sin(x: f64) -> (f64); => sin); (fn sincos(x: f64) -> (f64, f64); => sincos); @@ -188,22 +190,23 @@ libm_helper! { f16, funcs: { // verify-sorted-start - (fn ceilf(x: f16) -> (f16); => ceilf16); + (fn ceil(x: f16) -> (f16); => ceilf16); (fn copysign(x: f16, y: f16) -> (f16); => copysignf16); (fn fabs(x: f16) -> (f16); => fabsf16); (fn fdim(x: f16, y: f16) -> (f16); => fdimf16); - (fn floorf(x: f16) -> (f16); => floorf16); - (fn fmaxf(x: f16, y: f16) -> (f16); => fmaxf16); - (fn fmaximum_numf16(x: f16, y: f16) -> (f16); => fmaximum_numf16); + (fn floor(x: f16) -> (f16); => floorf16); + (fn fmax(x: f16, y: f16) -> (f16); => fmaxf16); + (fn fmaximum_num(x: f16, y: f16) -> (f16); => fmaximum_numf16); (fn fmaximumf16(x: f16, y: f16) -> (f16); => fmaximumf16); - (fn fminf(x: f16, y: f16) -> (f16); => fminf16); - (fn fminimum_numf16(x: f16, y: f16) -> (f16); => fminimum_numf16); - (fn fminimumf16(x: f16, y: f16) -> (f16); => fminimumf16); - (fn fmodf(x: f16, y: f16) -> (f16); => fmodf16); - (fn ldexpf16(x: f16, n: i32) -> (f16); => ldexpf16); - (fn rintf(x: f16) -> (f16); => rintf16); - (fn roundf(x: f16) -> (f16); => roundf16); - (fn scalbnf16(x: f16, n: i32) -> (f16); => ldexpf16); + (fn fmin(x: f16, y: f16) -> (f16); => fminf16); + (fn fminimum(x: f16, y: f16) -> (f16); => fminimumf16); + (fn fminimum_num(x: f16, y: f16) -> (f16); => fminimum_numf16); + (fn fmod(x: f16, y: f16) -> (f16); => fmodf16); + (fn ldexp(x: f16, n: i32) -> (f16); => ldexpf16); + (fn rint(x: f16) -> (f16); => rintf16); + (fn round(x: f16) -> (f16); => roundf16); + (fn roundeven(x: f16) -> (f16); => roundevenf16); + (fn scalbn(x: f16, n: i32) -> (f16); => scalbnf16); (fn sqrtf(x: f16) -> (f16); => sqrtf16); (fn truncf(x: f16) -> (f16); => truncf16); // verify-sorted-end @@ -220,18 +223,19 @@ libm_helper! { (fn fabs(x: f128) -> (f128); => fabsf128); (fn fdim(x: f128, y: f128) -> (f128); => fdimf128); (fn floor(x: f128) -> (f128); => floorf128); - (fn fmaf128(x: f128, y: f128, z: f128) -> (f128); => fmaf128); + (fn fma(x: f128, y: f128, z: f128) -> (f128); => fmaf128); (fn fmax(x: f128, y: f128) -> (f128); => fmaxf128); - (fn fmaximum_numf128(x: f128, y: f128) -> (f128); => fmaximum_numf128); - (fn fmaximumf128(x: f128, y: f128) -> (f128); => fmaximumf128); + (fn fmaximum(x: f128, y: f128) -> (f128); => fmaximumf128); + (fn fmaximum_num(x: f128, y: f128) -> (f128); => fmaximum_numf128); (fn fmin(x: f128, y: f128) -> (f128); => fminf128); - (fn fminimum_numf128(x: f128, y: f128) -> (f128); => fminimum_numf128); - (fn fminimumf128(x: f128, y: f128) -> (f128); => fminimumf128); + (fn fminimum(x: f128, y: f128) -> (f128); => fminimumf128); + (fn fminimum_num(x: f128, y: f128) -> (f128); => fminimum_numf128); (fn fmod(x: f128, y: f128) -> (f128); => fmodf128); - (fn ldexpf128(x: f128, n: i32) -> (f128); => ldexpf128); + (fn ldexp(x: f128, n: i32) -> (f128); => ldexpf128); (fn rint(x: f128) -> (f128); => rintf128); (fn round(x: f128) -> (f128); => roundf128); - (fn scalbnf128(x: f128, n: i32) -> (f128); => ldexpf128); + (fn roundeven(x: f128) -> (f128); => roundevenf128); + (fn scalbn(x: f128, n: i32) -> (f128); => scalbnf128); (fn sqrt(x: f128) -> (f128); => sqrtf128); (fn trunc(x: f128) -> (f128); => truncf128); // verify-sorted-end diff --git a/src/math/generic/mod.rs b/src/math/generic/mod.rs index 092f9317b..f224eba73 100644 --- a/src/math/generic/mod.rs +++ b/src/math/generic/mod.rs @@ -30,7 +30,7 @@ pub use fmin::fmin; pub use fminimum::fminimum; pub use fminimum_num::fminimum_num; pub use fmod::fmod; -pub use rint::rint; +pub use rint::rint_round; pub use round::round; pub use scalbn::scalbn; pub use sqrt::sqrt; diff --git a/src/math/generic/rint.rs b/src/math/generic/rint.rs index 80ba1faac..04e8f332f 100644 --- a/src/math/generic/rint.rs +++ b/src/math/generic/rint.rs @@ -2,27 +2,31 @@ /* origin: musl src/math/rint.c */ use super::super::Float; +use super::super::support::{FpResult, Round}; -pub fn rint(x: F) -> F { +/// IEEE 754-2019 `roundToIntegralExact`, which respects rounding mode and raises inexact if +/// applicable. +pub fn rint_round(x: F, _round: Round) -> FpResult { let toint = F::ONE / F::EPSILON; let e = x.exp(); let positive = x.is_sign_positive(); // On i386 `force_eval!` must be used to force rounding via storage to memory. Otherwise, // the excess precission from x87 would cause an incorrect final result. - let use_force = cfg!(x86_no_sse) && F::BITS == 32 || F::BITS == 64; + let force = |x| { + if cfg!(x86_no_sse) && (F::BITS == 32 || F::BITS == 64) { force_eval!(x) } else { x } + }; - if e >= F::EXP_BIAS + F::SIG_BITS { + let res = if e >= F::EXP_BIAS + F::SIG_BITS { // No fractional part; exact result can be returned. x } else { - // Apply a net-zero adjustment that nudges `y` in the direction of the rounding mode. + // Apply a net-zero adjustment that nudges `y` in the direction of the rounding mode. For + // Rust this is always nearest, but ideally it would take `round` into account. let y = if positive { - let tmp = if use_force { force_eval!(x) } else { x } + toint; - (if use_force { force_eval!(tmp) } else { tmp } - toint) + force(force(x) + toint) - toint } else { - let tmp = if use_force { force_eval!(x) } else { x } - toint; - (if use_force { force_eval!(tmp) } else { tmp } + toint) + force(force(x) - toint) + toint }; if y == F::ZERO { @@ -31,42 +35,85 @@ pub fn rint(x: F) -> F { } else { y } - } + }; + + FpResult::ok(res) } #[cfg(test)] mod tests { use super::*; + use crate::support::{Hexf, Int, Status}; + + fn spec_test(cases: &[(F, F, Status)]) { + let roundtrip = [F::ZERO, F::ONE, F::NEG_ONE, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY]; + + for x in roundtrip { + let FpResult { val, status } = rint_round(x, Round::Nearest); + assert_biteq!(val, x, "rint_round({})", Hexf(x)); + assert_eq!(status, Status::OK, "{}", Hexf(x)); + } + + for &(x, res, res_stat) in cases { + let FpResult { val, status } = rint_round(x, Round::Nearest); + assert_biteq!(val, res, "rint_round({})", Hexf(x)); + assert_eq!(status, res_stat, "{}", Hexf(x)); + } + } #[test] - fn zeroes_f32() { - assert_biteq!(rint(0.0_f32), 0.0_f32); - assert_biteq!(rint(-0.0_f32), -0.0_f32); + #[cfg(f16_enabled)] + fn spec_tests_f16() { + let cases = []; + spec_test::(&cases); } #[test] - fn sanity_check_f32() { - assert_biteq!(rint(-1.0_f32), -1.0); - assert_biteq!(rint(2.8_f32), 3.0); - assert_biteq!(rint(-0.5_f32), -0.0); - assert_biteq!(rint(0.5_f32), 0.0); - assert_biteq!(rint(-1.5_f32), -2.0); - assert_biteq!(rint(1.5_f32), 2.0); + fn spec_tests_f32() { + let cases = [ + (0.1, 0.0, Status::OK), + (-0.1, -0.0, Status::OK), + (0.5, 0.0, Status::OK), + (-0.5, -0.0, Status::OK), + (0.9, 1.0, Status::OK), + (-0.9, -1.0, Status::OK), + (1.1, 1.0, Status::OK), + (-1.1, -1.0, Status::OK), + (1.5, 2.0, Status::OK), + (-1.5, -2.0, Status::OK), + (1.9, 2.0, Status::OK), + (-1.9, -2.0, Status::OK), + (2.8, 3.0, Status::OK), + (-2.8, -3.0, Status::OK), + ]; + spec_test::(&cases); } #[test] - fn zeroes_f64() { - assert_biteq!(rint(0.0_f64), 0.0_f64); - assert_biteq!(rint(-0.0_f64), -0.0_f64); + fn spec_tests_f64() { + let cases = [ + (0.1, 0.0, Status::OK), + (-0.1, -0.0, Status::OK), + (0.5, 0.0, Status::OK), + (-0.5, -0.0, Status::OK), + (0.9, 1.0, Status::OK), + (-0.9, -1.0, Status::OK), + (1.1, 1.0, Status::OK), + (-1.1, -1.0, Status::OK), + (1.5, 2.0, Status::OK), + (-1.5, -2.0, Status::OK), + (1.9, 2.0, Status::OK), + (-1.9, -2.0, Status::OK), + (2.8, 3.0, Status::OK), + (-2.8, -3.0, Status::OK), + ]; + spec_test::(&cases); } #[test] - fn sanity_check_f64() { - assert_biteq!(rint(-1.0_f64), -1.0); - assert_biteq!(rint(2.8_f64), 3.0); - assert_biteq!(rint(-0.5_f64), -0.0); - assert_biteq!(rint(0.5_f64), 0.0); - assert_biteq!(rint(-1.5_f64), -2.0); - assert_biteq!(rint(1.5_f64), 2.0); + #[cfg(f128_enabled)] + fn spec_tests_f128() { + let cases = []; + spec_test::(&cases); } } diff --git a/src/math/mod.rs b/src/math/mod.rs index 4e75292a6..e58d79adc 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -207,8 +207,8 @@ mod remainderf; mod remquo; mod remquof; mod rint; -mod rintf; mod round; +mod roundeven; mod roundf; mod scalbn; mod scalbnf; @@ -313,9 +313,9 @@ pub use self::remainder::remainder; pub use self::remainderf::remainderf; pub use self::remquo::remquo; pub use self::remquof::remquof; -pub use self::rint::rint; -pub use self::rintf::rintf; +pub use self::rint::{rint, rintf}; pub use self::round::round; +pub use self::roundeven::{roundeven, roundevenf}; pub use self::roundf::roundf; pub use self::scalbn::scalbn; pub use self::scalbnf::scalbnf; @@ -346,7 +346,6 @@ cfg_if! { mod floorf16; mod fmodf16; mod ldexpf16; - mod rintf16; mod roundf16; mod scalbnf16; mod sqrtf16; @@ -364,7 +363,8 @@ cfg_if! { pub use self::fminimum_fmaximum_num::{fmaximum_numf16, fminimum_numf16}; pub use self::fmodf16::fmodf16; pub use self::ldexpf16::ldexpf16; - pub use self::rintf16::rintf16; + pub use self::rint::rintf16; + pub use self::roundeven::roundevenf16; pub use self::roundf16::roundf16; pub use self::scalbnf16::scalbnf16; pub use self::sqrtf16::sqrtf16; @@ -384,7 +384,6 @@ cfg_if! { mod fmaf128; mod fmodf128; mod ldexpf128; - mod rintf128; mod roundf128; mod scalbnf128; mod sqrtf128; @@ -403,7 +402,8 @@ cfg_if! { pub use self::fminimum_fmaximum_num::{fmaximum_numf128, fminimum_numf128}; pub use self::fmodf128::fmodf128; pub use self::ldexpf128::ldexpf128; - pub use self::rintf128::rintf128; + pub use self::rint::rintf128; + pub use self::roundeven::roundevenf128; pub use self::roundf128::roundf128; pub use self::scalbnf128::scalbnf128; pub use self::sqrtf128::sqrtf128; diff --git a/src/math/rint.rs b/src/math/rint.rs index f409ec282..8a5cbeab4 100644 --- a/src/math/rint.rs +++ b/src/math/rint.rs @@ -1,3 +1,27 @@ +use super::support::Round; + +/// Round `x` to the nearest integer, breaking ties toward even. +#[cfg(f16_enabled)] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn rintf16(x: f16) -> f16 { + super::generic::rint_round(x, Round::Nearest).val +} + +/// Round `x` to the nearest integer, breaking ties toward even. +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn rintf(x: f32) -> f32 { + select_implementation! { + name: rintf, + use_arch: any( + all(target_arch = "wasm32", intrinsics_enabled), + all(target_arch = "aarch64", target_feature = "neon", target_endian = "little"), + ), + args: x, + } + + super::generic::rint_round(x, Round::Nearest).val +} + /// Round `x` to the nearest integer, breaking ties toward even. #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn rint(x: f64) -> f64 { @@ -10,5 +34,12 @@ pub fn rint(x: f64) -> f64 { args: x, } - super::generic::rint(x) + super::generic::rint_round(x, Round::Nearest).val +} + +/// Round `x` to the nearest integer, breaking ties toward even. +#[cfg(f128_enabled)] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn rintf128(x: f128) -> f128 { + super::generic::rint_round(x, Round::Nearest).val } diff --git a/src/math/rintf.rs b/src/math/rintf.rs deleted file mode 100644 index 5e9f5f718..000000000 --- a/src/math/rintf.rs +++ /dev/null @@ -1,14 +0,0 @@ -/// Round `x` to the nearest integer, breaking ties toward even. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn rintf(x: f32) -> f32 { - select_implementation! { - name: rintf, - use_arch: any( - all(target_arch = "wasm32", intrinsics_enabled), - all(target_arch = "aarch64", target_feature = "neon", target_endian = "little"), - ), - args: x, - } - - super::generic::rint(x) -} diff --git a/src/math/rintf128.rs b/src/math/rintf128.rs deleted file mode 100644 index 6b16fcd84..000000000 --- a/src/math/rintf128.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Round `x` to the nearest integer, breaking ties toward even. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn rintf128(x: f128) -> f128 { - super::generic::rint(x) -} diff --git a/src/math/rintf16.rs b/src/math/rintf16.rs deleted file mode 100644 index 84d792561..000000000 --- a/src/math/rintf16.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Round `x` to the nearest integer, breaking ties toward even. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn rintf16(x: f16) -> f16 { - super::generic::rint(x) -} diff --git a/src/math/roundeven.rs b/src/math/roundeven.rs new file mode 100644 index 000000000..ec1738285 --- /dev/null +++ b/src/math/roundeven.rs @@ -0,0 +1,35 @@ +use super::support::{Float, Round}; + +/// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 +/// `roundToIntegralTiesToEven`. +#[cfg(f16_enabled)] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn roundevenf16(x: f16) -> f16 { + roundeven_impl(x) +} + +/// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 +/// `roundToIntegralTiesToEven`. +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn roundevenf(x: f32) -> f32 { + roundeven_impl(x) +} + +/// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 +/// `roundToIntegralTiesToEven`. +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn roundeven(x: f64) -> f64 { + roundeven_impl(x) +} + +/// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 +/// `roundToIntegralTiesToEven`. +#[cfg(f128_enabled)] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn roundevenf128(x: f128) -> f128 { + roundeven_impl(x) +} + +pub fn roundeven_impl(x: F) -> F { + super::generic::rint_round(x, Round::Nearest).val +}