Add a generic version of rint
Use this to implement `rint` and `rintf`.
This commit is contained in:
parent
3a96a55e70
commit
bf2d171b1a
5 changed files with 78 additions and 94 deletions
|
|
@ -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,6 +663,7 @@
|
|||
"sources": [
|
||||
"src/math/arch/aarch64.rs",
|
||||
"src/math/arch/wasm32.rs",
|
||||
"src/math/generic/rint.rs",
|
||||
"src/math/rintf.rs"
|
||||
],
|
||||
"type": "f32"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
72
library/compiler-builtins/libm/src/math/generic/rint.rs
Normal file
72
library/compiler-builtins/libm/src/math/generic/rint.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,51 +9,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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,51 +9,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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue