Merge pull request rust-lang/libm#437 from tgross35/generic-floor

Add `floorf16` and `floorf128`
This commit is contained in:
Trevor Gross 2025-01-22 06:02:17 -05:00 committed by GitHub
commit 3a96a55e70
17 changed files with 175 additions and 100 deletions

View file

@ -67,6 +67,8 @@ no_mangle! {
cbrtf(x: f32) -> f32;
ceil(x: f64) -> f64;
ceilf(x: f32) -> f32;
ceilf128(x: f128) -> f128;
ceilf16(x: f16) -> f16;
copysign(x: f64, y: f64) -> f64;
copysignf(x: f32, y: f32) -> f32;
copysignf128(x: f128, y: f128) -> f128;
@ -97,6 +99,8 @@ no_mangle! {
fdimf16(x: f16, y: f16) -> f16;
floor(x: f64) -> f64;
floorf(x: f32) -> f32;
floorf128(x: f128) -> f128;
floorf16(x: f16) -> f16;
fma(x: f64, y: f64, z: f64) -> f64;
fmaf(x: f32, y: f32, z: f32) -> f32;
fmax(x: f64, y: f64) -> 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", "sqrtf16", "truncf16"],
&["ceilf16", "fabsf16", "floorf16", "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", "sqrtf128", "truncf128"],
&["ceilf128", "fabsf128", "floorf128", "sqrtf128", "truncf128"],
),
(
// `(f16, f16) -> f16`

View file

@ -101,6 +101,8 @@ main!(
icount_bench_fdimf16_group,
icount_bench_fdimf_group,
icount_bench_floor_group,
icount_bench_floorf128_group,
icount_bench_floorf16_group,
icount_bench_floorf_group,
icount_bench_fma_group,
icount_bench_fmaf_group,

View file

@ -125,8 +125,10 @@ libm_macros::for_each_function! {
| fabsf16
| fdimf128
| fdimf16
| sqrtf16
| floorf128
| floorf16
| sqrtf128
| sqrtf16
| truncf128
| truncf16 => (false, None),

View file

@ -148,6 +148,8 @@ libm_macros::for_each_function! {
fabsf128,
fabsf16,floor,
floorf,
floorf128,
floorf16,
fmod,
fmodf,
frexp,
@ -240,6 +242,7 @@ impl_no_round! {
impl_no_round! {
fabsf16 => abs_mut;
ceilf16 => ceil_mut;
floorf16 => floor_mut;
truncf16 => trunc_mut;
}
@ -247,6 +250,7 @@ impl_no_round! {
impl_no_round! {
fabsf128 => abs_mut;
ceilf128 => ceil_mut;
floorf128 => floor_mut;
truncf128 => trunc_mut;
}

View file

@ -87,6 +87,8 @@ libm_macros::for_each_function! {
fabsf16,
fdimf128,
fdimf16,
floorf128,
floorf16,
truncf128,
truncf16,
sqrtf16,

View file

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

View file

@ -336,17 +336,33 @@
"src/libm_helper.rs",
"src/math/arch/i586.rs",
"src/math/arch/wasm32.rs",
"src/math/floor.rs"
"src/math/floor.rs",
"src/math/generic/floor.rs"
],
"type": "f64"
},
"floorf": {
"sources": [
"src/math/arch/wasm32.rs",
"src/math/floorf.rs"
"src/math/floorf.rs",
"src/math/generic/floor.rs"
],
"type": "f32"
},
"floorf128": {
"sources": [
"src/math/floorf128.rs",
"src/math/generic/floor.rs"
],
"type": "f128"
},
"floorf16": {
"sources": [
"src/math/floorf16.rs",
"src/math/generic/floor.rs"
],
"type": "f16"
},
"fma": {
"sources": [
"src/libm_helper.rs",

View file

@ -49,6 +49,8 @@ fdimf128
fdimf16
floor
floorf
floorf128
floorf16
fma
fmaf
fmax

View file

@ -1,8 +1,3 @@
#![allow(unreachable_code)]
use core::f64;
const TOINT: f64 = 1. / f64::EPSILON;
/// Floor (f64)
///
/// Finds the nearest integer less than or equal to `x`.
@ -15,39 +10,5 @@ pub fn floor(x: f64) -> f64 {
args: x,
}
let ui = x.to_bits();
let e = ((ui >> 52) & 0x7ff) as i32;
if (e >= 0x3ff + 52) || (x == 0.) {
return x;
}
/* y = int(x) - x, where int(x) is an integer neighbor of x */
let y = if (ui >> 63) != 0 { x - TOINT + TOINT - x } else { x + TOINT - TOINT - x };
/* special case because of non-nearest rounding modes */
if e < 0x3ff {
force_eval!(y);
return if (ui >> 63) != 0 { -1. } else { 0. };
}
if y > 0. { x + y - 1. } else { x + y }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sanity_check() {
assert_eq!(floor(1.1), 1.0);
assert_eq!(floor(2.9), 2.0);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/floor
#[test]
fn spec_tests() {
// Not Asserted: that the current rounding mode has no effect.
assert!(floor(f64::NAN).is_nan());
for f in [0.0, -0.0, f64::INFINITY, f64::NEG_INFINITY].iter().copied() {
assert_eq!(floor(f), f);
}
}
return super::generic::floor(x);
}

View file

@ -1,5 +1,3 @@
use core::f32;
/// Floor (f32)
///
/// Finds the nearest integer less than or equal to `x`.
@ -11,53 +9,5 @@ pub fn floorf(x: f32) -> f32 {
args: x,
}
let mut ui = x.to_bits();
let e = (((ui >> 23) as i32) & 0xff) - 0x7f;
if e >= 23 {
return x;
}
if e >= 0 {
let m: u32 = 0x007fffff >> e;
if (ui & m) == 0 {
return x;
}
force_eval!(x + f32::from_bits(0x7b800000));
if ui >> 31 != 0 {
ui += m;
}
ui &= !m;
} else {
force_eval!(x + f32::from_bits(0x7b800000));
if ui >> 31 == 0 {
ui = 0;
} else if ui << 1 != 0 {
return -1.0;
}
}
f32::from_bits(ui)
}
// 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::*;
#[test]
fn sanity_check() {
assert_eq!(floorf(0.5), 0.0);
assert_eq!(floorf(1.1), 1.0);
assert_eq!(floorf(2.9), 2.0);
}
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/floor
#[test]
fn spec_tests() {
// Not Asserted: that the current rounding mode has no effect.
assert!(floorf(f32::NAN).is_nan());
for f in [0.0, -0.0, f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
assert_eq!(floorf(f), f);
}
}
return super::generic::floor(x);
}

View file

@ -0,0 +1,7 @@
/// Floor (f128)
///
/// Finds the nearest integer less than or equal to `x`.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn floorf128(x: f128) -> f128 {
return super::generic::floor(x);
}

View file

@ -0,0 +1,7 @@
/// Floor (f16)
///
/// Finds the nearest integer less than or equal to `x`.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn floorf16(x: f16) -> f16 {
return super::generic::floor(x);
}

View file

@ -31,24 +31,28 @@ pub fn ceil<F: Float>(x: F) -> F {
// Otherwise, raise an inexact exception.
force_eval!(x + F::MAX);
if x.is_sign_positive() {
ix += m;
}
ix &= !m;
F::from_bits(ix)
} else {
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
force_eval!(x + F::MAX);
if x.is_sign_negative() {
// -1.0 < x <= -0.0; rounding up goes toward -0.0.
return F::NEG_ZERO;
F::NEG_ZERO
} else if ix << 1 != zero {
// 0.0 < x < 1.0; rounding up goes toward +1.0.
return F::ONE;
F::ONE
} else {
// +0.0 remains unchanged
x
}
}
F::from_bits(ix)
}
#[cfg(test)]

View file

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: MIT
* origin: musl src/math/floor.c */
//! Generic `floor` algorithm.
//!
//! Note that this uses the algorithm from musl's `floorf` rather than `floor` or `floorl` because
//! performance seems to be better (based on icount) and it does not seem to experience rounding
//! errors on i386.
use super::super::{Float, Int, IntTy, MinInt};
pub fn floor<F: Float>(x: F) -> F {
let zero = IntTy::<F>::ZERO;
let mut ix = x.to_bits();
let e = x.exp_unbiased();
// If the represented value has no fractional part, no truncation is needed.
if e >= F::SIG_BITS as i32 {
return x;
}
if e >= 0 {
// |x| >= 1.0
let m = F::SIG_MASK >> e.unsigned();
if ix & m == zero {
// Portion to be masked is already zero; no adjustment needed.
return x;
}
// Otherwise, raise an inexact exception.
force_eval!(x + F::MAX);
if x.is_sign_negative() {
ix += m;
}
ix &= !m;
F::from_bits(ix)
} else {
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
force_eval!(x + F::MAX);
if x.is_sign_positive() {
// 0.0 <= x < 1.0; rounding down goes toward +0.0.
F::ZERO
} else if ix << 1 != zero {
// -1.0 < x < 0.0; rounding down goes toward -1.0.
F::NEG_ONE
} else {
// -0.0 remains unchanged
x
}
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Test against https://en.cppreference.com/w/cpp/numeric/math/floor
fn spec_test<F: Float>() {
// Not Asserted: that the current rounding mode has no effect.
for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() {
assert_biteq!(floor(f), f);
}
}
/* Skipping f16 / f128 "sanity_check"s due to rejected literal lexing at MSRV */
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
spec_test::<f16>();
}
#[test]
fn sanity_check_f32() {
assert_eq!(floor(0.5f32), 0.0);
assert_eq!(floor(1.1f32), 1.0);
assert_eq!(floor(2.9f32), 2.0);
}
#[test]
fn spec_tests_f32() {
spec_test::<f32>();
}
#[test]
fn sanity_check_f64() {
assert_eq!(floor(1.1f64), 1.0);
assert_eq!(floor(2.9f64), 2.0);
}
#[test]
fn spec_tests_f64() {
spec_test::<f64>();
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
spec_test::<f128>();
}
}

View file

@ -2,6 +2,7 @@ mod ceil;
mod copysign;
mod fabs;
mod fdim;
mod floor;
mod sqrt;
mod trunc;
@ -9,5 +10,6 @@ pub use ceil::ceil;
pub use copysign::copysign;
pub use fabs::fabs;
pub use fdim::fdim;
pub use floor::floor;
pub use sqrt::sqrt;
pub use trunc::trunc;

View file

@ -345,6 +345,7 @@ cfg_if! {
mod copysignf16;
mod fabsf16;
mod fdimf16;
mod floorf16;
mod sqrtf16;
mod truncf16;
@ -352,6 +353,7 @@ cfg_if! {
pub use self::copysignf16::copysignf16;
pub use self::fabsf16::fabsf16;
pub use self::fdimf16::fdimf16;
pub use self::floorf16::floorf16;
pub use self::sqrtf16::sqrtf16;
pub use self::truncf16::truncf16;
}
@ -363,6 +365,7 @@ cfg_if! {
mod copysignf128;
mod fabsf128;
mod fdimf128;
mod floorf128;
mod sqrtf128;
mod truncf128;
@ -370,6 +373,7 @@ cfg_if! {
pub use self::copysignf128::copysignf128;
pub use self::fabsf128::fabsf128;
pub use self::fdimf128::fdimf128;
pub use self::floorf128::floorf128;
pub use self::sqrtf128::sqrtf128;
pub use self::truncf128::truncf128;
}