Merge pull request rust-lang/libm#454 from tgross35/generic-rint

Add `rintf16` and `rintf128`
This commit is contained in:
Trevor Gross 2025-01-22 06:31:35 -05:00 committed by GitHub
commit 6c6354bbe1
16 changed files with 130 additions and 100 deletions

View file

@ -145,6 +145,8 @@ no_mangle! {
remquof(x: f32, y: f32 | q: &mut c_int) -> f32;
rint(x: f64) -> f64;
rintf(x: f32) -> f32;
rintf128(x: f128) -> f128;
rintf16(x: f16) -> f16;
round(x: f64) -> f64;
roundf(x: f32) -> f32;
scalbn(x: f64, y: c_int) -> f64;

View file

@ -9,7 +9,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
FloatTy::F16,
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
None,
&["ceilf16", "fabsf16", "floorf16", "sqrtf16", "truncf16"],
&["ceilf16", "fabsf16", "floorf16", "rintf16", "sqrtf16", "truncf16"],
),
(
// `fn(f32) -> f32`
@ -40,7 +40,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
FloatTy::F128,
Signature { args: &[Ty::F128], returns: &[Ty::F128] },
None,
&["ceilf128", "fabsf128", "floorf128", "sqrtf128", "truncf128"],
&["ceilf128", "fabsf128", "floorf128", "rintf128", "sqrtf128", "truncf128"],
),
(
// `(f16, f16) -> f16`

View file

@ -149,6 +149,8 @@ main!(
icount_bench_remquo_group,
icount_bench_remquof_group,
icount_bench_rint_group,
icount_bench_rintf128_group,
icount_bench_rintf16_group,
icount_bench_rintf_group,
icount_bench_round_group,
icount_bench_roundf_group,

View file

@ -127,6 +127,8 @@ libm_macros::for_each_function! {
| fdimf16
| floorf128
| floorf16
| rintf128
| rintf16
| sqrtf128
| sqrtf16
| truncf128

View file

@ -170,6 +170,8 @@ libm_macros::for_each_function! {
remquof,
rint,
rintf,
rintf128,
rintf16,
round,
roundf,
scalbn,
@ -240,17 +242,19 @@ impl_no_round! {
#[cfg(f16_enabled)]
impl_no_round! {
fabsf16 => abs_mut;
ceilf16 => ceil_mut;
fabsf16 => abs_mut;
floorf16 => floor_mut;
rintf16 => round_even_mut; // FIXME: respect rounding mode
truncf16 => trunc_mut;
}
#[cfg(f128_enabled)]
impl_no_round! {
fabsf128 => abs_mut;
ceilf128 => ceil_mut;
fabsf128 => abs_mut;
floorf128 => floor_mut;
rintf128 => round_even_mut; // FIXME: respect rounding mode
truncf128 => trunc_mut;
}

View file

@ -89,9 +89,11 @@ libm_macros::for_each_function! {
fdimf16,
floorf128,
floorf16,
rintf128,
rintf16,
sqrtf128,
sqrtf16,
truncf128,
truncf16,
sqrtf16,
sqrtf128,
],
}

View file

@ -94,6 +94,8 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
| fdimf16
| floorf128
| floorf16
| rintf128
| rintf16
| sqrtf128
| sqrtf16
| truncf128

View file

@ -654,6 +654,7 @@
"src/libm_helper.rs",
"src/math/arch/aarch64.rs",
"src/math/arch/wasm32.rs",
"src/math/generic/rint.rs",
"src/math/rint.rs"
],
"type": "f64"
@ -662,10 +663,25 @@
"sources": [
"src/math/arch/aarch64.rs",
"src/math/arch/wasm32.rs",
"src/math/generic/rint.rs",
"src/math/rintf.rs"
],
"type": "f32"
},
"rintf128": {
"sources": [
"src/math/generic/rint.rs",
"src/math/rintf128.rs"
],
"type": "f128"
},
"rintf16": {
"sources": [
"src/math/generic/rint.rs",
"src/math/rintf16.rs"
],
"type": "f16"
},
"round": {
"sources": [
"src/libm_helper.rs",

View file

@ -97,6 +97,8 @@ remquo
remquof
rint
rintf
rintf128
rintf16
round
roundf
scalbn

View file

@ -3,6 +3,7 @@ mod copysign;
mod fabs;
mod fdim;
mod floor;
mod rint;
mod sqrt;
mod trunc;
@ -11,5 +12,6 @@ pub use copysign::copysign;
pub use fabs::fabs;
pub use fdim::fdim;
pub use floor::floor;
pub use rint::rint;
pub use sqrt::sqrt;
pub use trunc::trunc;

View file

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: MIT */
/* origin: musl src/math/rint.c */
use super::super::Float;
pub fn rint<F: Float>(x: F) -> F {
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;
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.
let y = if positive {
let tmp = if use_force { force_eval!(x) } else { x } + toint;
(if use_force { force_eval!(tmp) } else { tmp } - toint)
} else {
let tmp = if use_force { force_eval!(x) } else { x } - toint;
(if use_force { force_eval!(tmp) } else { tmp } + toint)
};
if y == F::ZERO {
// A zero result takes the sign of the input.
if positive { F::ZERO } else { F::NEG_ZERO }
} else {
y
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zeroes_f32() {
assert_biteq!(rint(0.0_f32), 0.0_f32);
assert_biteq!(rint(-0.0_f32), -0.0_f32);
}
#[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);
}
#[test]
fn zeroes_f64() {
assert_biteq!(rint(0.0_f64), 0.0_f64);
assert_biteq!(rint(-0.0_f64), -0.0_f64);
}
#[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);
}
}

View file

@ -346,6 +346,7 @@ cfg_if! {
mod fabsf16;
mod fdimf16;
mod floorf16;
mod rintf16;
mod sqrtf16;
mod truncf16;
@ -354,6 +355,7 @@ cfg_if! {
pub use self::fabsf16::fabsf16;
pub use self::fdimf16::fdimf16;
pub use self::floorf16::floorf16;
pub use self::rintf16::rintf16;
pub use self::sqrtf16::sqrtf16;
pub use self::truncf16::truncf16;
}
@ -366,6 +368,7 @@ cfg_if! {
mod fabsf128;
mod fdimf128;
mod floorf128;
mod rintf128;
mod sqrtf128;
mod truncf128;
@ -374,6 +377,7 @@ cfg_if! {
pub use self::fabsf128::fabsf128;
pub use self::fdimf128::fdimf128;
pub use self::floorf128::floorf128;
pub use self::rintf128::rintf128;
pub use self::sqrtf128::sqrtf128;
pub use self::truncf128::truncf128;
}

View file

@ -1,3 +1,4 @@
/// 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 {
select_implementation! {
@ -9,51 +10,5 @@ pub fn rint(x: f64) -> f64 {
args: x,
}
let one_over_e = 1.0 / f64::EPSILON;
let as_u64: u64 = x.to_bits();
let exponent: u64 = (as_u64 >> 52) & 0x7ff;
let is_positive = (as_u64 >> 63) == 0;
if exponent >= 0x3ff + 52 {
x
} else {
let ans = if is_positive {
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let x = force_eval!(x);
let xplusoneovere = x + one_over_e;
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let xplusoneovere = force_eval!(xplusoneovere);
xplusoneovere - one_over_e
} else {
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let x = force_eval!(x);
let xminusoneovere = x - one_over_e;
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let xminusoneovere = force_eval!(xminusoneovere);
xminusoneovere + one_over_e
};
if ans == 0.0 { if is_positive { 0.0 } else { -0.0 } } else { ans }
}
}
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
#[cfg(not(target_arch = "powerpc64"))]
#[cfg(test)]
mod tests {
use super::rint;
#[test]
fn negative_zero() {
assert_eq!(rint(-0.0_f64).to_bits(), (-0.0_f64).to_bits());
}
#[test]
fn sanity_check() {
assert_eq!(rint(-1.0), -1.0);
assert_eq!(rint(2.8), 3.0);
assert_eq!(rint(-0.5), -0.0);
assert_eq!(rint(0.5), 0.0);
assert_eq!(rint(-1.5), -2.0);
assert_eq!(rint(1.5), 2.0);
}
super::generic::rint(x)
}

View file

@ -1,3 +1,4 @@
/// 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! {
@ -9,51 +10,5 @@ pub fn rintf(x: f32) -> f32 {
args: x,
}
let one_over_e = 1.0 / f32::EPSILON;
let as_u32: u32 = x.to_bits();
let exponent: u32 = (as_u32 >> 23) & 0xff;
let is_positive = (as_u32 >> 31) == 0;
if exponent >= 0x7f + 23 {
x
} else {
let ans = if is_positive {
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let x = force_eval!(x);
let xplusoneovere = x + one_over_e;
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let xplusoneovere = force_eval!(xplusoneovere);
xplusoneovere - one_over_e
} else {
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let x = force_eval!(x);
let xminusoneovere = x - one_over_e;
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
let xminusoneovere = force_eval!(xminusoneovere);
xminusoneovere + one_over_e
};
if ans == 0.0 { if is_positive { 0.0 } else { -0.0 } } else { ans }
}
}
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
#[cfg(not(target_arch = "powerpc64"))]
#[cfg(test)]
mod tests {
use super::rintf;
#[test]
fn negative_zero() {
assert_eq!(rintf(-0.0_f32).to_bits(), (-0.0_f32).to_bits());
}
#[test]
fn sanity_check() {
assert_eq!(rintf(-1.0), -1.0);
assert_eq!(rintf(2.8), 3.0);
assert_eq!(rintf(-0.5), -0.0);
assert_eq!(rintf(0.5), 0.0);
assert_eq!(rintf(-1.5), -2.0);
assert_eq!(rintf(1.5), 2.0);
}
super::generic::rint(x)
}

View file

@ -0,0 +1,5 @@
/// 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)
}

View file

@ -0,0 +1,5 @@
/// 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)
}