Merge pull request rust-lang/libm#436 from tgross35/generic-ceil
Add `ceilf16` and `ceilf128`
This commit is contained in:
commit
7a357b96c0
18 changed files with 168 additions and 102 deletions
|
|
@ -9,7 +9,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
FloatTy::F16,
|
||||
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
|
||||
None,
|
||||
&["fabsf16", "sqrtf16", "truncf16"],
|
||||
&["ceilf16", "fabsf16", "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,
|
||||
&["fabsf128", "sqrtf128", "truncf128"],
|
||||
&["ceilf128", "fabsf128", "sqrtf128", "truncf128"],
|
||||
),
|
||||
(
|
||||
// `(f16, f16) -> f16`
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ main!(
|
|||
icount_bench_cbrt_group,
|
||||
icount_bench_cbrtf_group,
|
||||
icount_bench_ceil_group,
|
||||
icount_bench_ceilf128_group,
|
||||
icount_bench_ceilf16_group,
|
||||
icount_bench_ceilf_group,
|
||||
icount_bench_copysign_group,
|
||||
icount_bench_copysignf128_group,
|
||||
|
|
|
|||
|
|
@ -117,7 +117,9 @@ libm_macros::for_each_function! {
|
|||
exp10 | exp10f | exp2 | exp2f => (true, Some(musl_math_sys::MACRO_FN_NAME)),
|
||||
|
||||
// Musl does not provide `f16` and `f128` functions
|
||||
copysignf128
|
||||
ceilf128
|
||||
| ceilf16
|
||||
| copysignf128
|
||||
| copysignf16
|
||||
| fabsf128
|
||||
| fabsf16
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ impl Float for f8 {
|
|||
const INFINITY: Self = Self(0b0_1111_000);
|
||||
const NEG_INFINITY: Self = Self(0b1_1111_000);
|
||||
const NAN: Self = Self(0b0_1111_100);
|
||||
// FIXME: incorrect values
|
||||
const EPSILON: Self = Self::ZERO;
|
||||
const PI: Self = Self::ZERO;
|
||||
const NEG_PI: Self = Self::ZERO;
|
||||
const FRAC_PI_2: Self = Self::ZERO;
|
||||
|
|
|
|||
|
|
@ -137,6 +137,8 @@ libm_macros::for_each_function! {
|
|||
// Most of these need a manual implementation
|
||||
ceil,
|
||||
ceilf,
|
||||
ceilf128,
|
||||
ceilf16,
|
||||
copysign,
|
||||
copysignf,
|
||||
copysignf128,
|
||||
|
|
@ -237,12 +239,14 @@ impl_no_round! {
|
|||
#[cfg(f16_enabled)]
|
||||
impl_no_round! {
|
||||
fabsf16 => abs_mut;
|
||||
ceilf16 => ceil_mut;
|
||||
truncf16 => trunc_mut;
|
||||
}
|
||||
|
||||
#[cfg(f128_enabled)]
|
||||
impl_no_round! {
|
||||
fabsf128 => abs_mut;
|
||||
ceilf128 => ceil_mut;
|
||||
truncf128 => trunc_mut;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ libm_macros::for_each_function! {
|
|||
ynf,
|
||||
|
||||
// Not provided by musl
|
||||
ceilf128,
|
||||
ceilf16,
|
||||
copysignf128,
|
||||
copysignf16,
|
||||
fabsf128,
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
|
|||
emit_types: [CFn, RustFn, RustArgs],
|
||||
extra: (basis, op, inputs),
|
||||
fn_extra: match MACRO_FN_NAME {
|
||||
copysignf128
|
||||
ceilf128
|
||||
| ceilf16
|
||||
| copysignf128
|
||||
| copysignf16
|
||||
| fabsf128
|
||||
| fabsf16
|
||||
|
|
|
|||
|
|
@ -109,17 +109,33 @@
|
|||
"src/libm_helper.rs",
|
||||
"src/math/arch/i586.rs",
|
||||
"src/math/arch/wasm32.rs",
|
||||
"src/math/ceil.rs"
|
||||
"src/math/ceil.rs",
|
||||
"src/math/generic/ceil.rs"
|
||||
],
|
||||
"type": "f64"
|
||||
},
|
||||
"ceilf": {
|
||||
"sources": [
|
||||
"src/math/arch/wasm32.rs",
|
||||
"src/math/ceilf.rs"
|
||||
"src/math/ceilf.rs",
|
||||
"src/math/generic/ceil.rs"
|
||||
],
|
||||
"type": "f32"
|
||||
},
|
||||
"ceilf128": {
|
||||
"sources": [
|
||||
"src/math/ceilf128.rs",
|
||||
"src/math/generic/ceil.rs"
|
||||
],
|
||||
"type": "f128"
|
||||
},
|
||||
"ceilf16": {
|
||||
"sources": [
|
||||
"src/math/ceilf16.rs",
|
||||
"src/math/generic/ceil.rs"
|
||||
],
|
||||
"type": "f16"
|
||||
},
|
||||
"copysign": {
|
||||
"sources": [
|
||||
"src/libm_helper.rs",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ cbrt
|
|||
cbrtf
|
||||
ceil
|
||||
ceilf
|
||||
ceilf128
|
||||
ceilf16
|
||||
copysign
|
||||
copysignf
|
||||
copysignf128
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
#![allow(unreachable_code)]
|
||||
use core::f64;
|
||||
|
||||
const TOINT: f64 = 1. / f64::EPSILON;
|
||||
|
||||
/// Ceil (f64)
|
||||
///
|
||||
/// Finds the nearest integer greater than or equal to `x`.
|
||||
|
|
@ -15,40 +10,5 @@ pub fn ceil(x: f64) -> f64 {
|
|||
args: x,
|
||||
}
|
||||
|
||||
let u: u64 = x.to_bits();
|
||||
let e: i64 = ((u >> 52) & 0x7ff) as i64;
|
||||
let y: f64;
|
||||
|
||||
if e >= 0x3ff + 52 || x == 0. {
|
||||
return x;
|
||||
}
|
||||
// y = int(x) - x, where int(x) is an integer neighbor of x
|
||||
y = if (u >> 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 (u >> 63) != 0 { -0. } else { 1. };
|
||||
}
|
||||
if y < 0. { x + y + 1. } else { x + y }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sanity_check() {
|
||||
assert_eq!(ceil(1.1), 2.0);
|
||||
assert_eq!(ceil(2.9), 3.0);
|
||||
}
|
||||
|
||||
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
|
||||
#[test]
|
||||
fn spec_tests() {
|
||||
// Not Asserted: that the current rounding mode has no effect.
|
||||
assert!(ceil(f64::NAN).is_nan());
|
||||
for f in [0.0, -0.0, f64::INFINITY, f64::NEG_INFINITY].iter().copied() {
|
||||
assert_eq!(ceil(f), f);
|
||||
}
|
||||
}
|
||||
super::generic::ceil(x)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use core::f32;
|
||||
|
||||
/// Ceil (f32)
|
||||
///
|
||||
/// Finds the nearest integer greater than or equal to `x`.
|
||||
|
|
@ -11,52 +9,5 @@ pub fn ceilf(x: f32) -> f32 {
|
|||
args: x,
|
||||
}
|
||||
|
||||
let mut ui = x.to_bits();
|
||||
let e = (((ui >> 23) & 0xff).wrapping_sub(0x7f)) as i32;
|
||||
|
||||
if e >= 23 {
|
||||
return x;
|
||||
}
|
||||
if e >= 0 {
|
||||
let m = 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 {
|
||||
return -0.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!(ceilf(1.1), 2.0);
|
||||
assert_eq!(ceilf(2.9), 3.0);
|
||||
}
|
||||
|
||||
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
|
||||
#[test]
|
||||
fn spec_tests() {
|
||||
// Not Asserted: that the current rounding mode has no effect.
|
||||
assert!(ceilf(f32::NAN).is_nan());
|
||||
for f in [0.0, -0.0, f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
|
||||
assert_eq!(ceilf(f), f);
|
||||
}
|
||||
}
|
||||
super::generic::ceil(x)
|
||||
}
|
||||
|
|
|
|||
7
library/compiler-builtins/libm/src/math/ceilf128.rs
Normal file
7
library/compiler-builtins/libm/src/math/ceilf128.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/// Ceil (f128)
|
||||
///
|
||||
/// Finds the nearest integer greater than or equal to `x`.
|
||||
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
||||
pub fn ceilf128(x: f128) -> f128 {
|
||||
super::generic::ceil(x)
|
||||
}
|
||||
7
library/compiler-builtins/libm/src/math/ceilf16.rs
Normal file
7
library/compiler-builtins/libm/src/math/ceilf16.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/// Ceil (f16)
|
||||
///
|
||||
/// Finds the nearest integer greater than or equal to `x`.
|
||||
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
||||
pub fn ceilf16(x: f16) -> f16 {
|
||||
super::generic::ceil(x)
|
||||
}
|
||||
101
library/compiler-builtins/libm/src/math/generic/ceil.rs
Normal file
101
library/compiler-builtins/libm/src/math/generic/ceil.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/* origin: musl src/math/ceilf.c */
|
||||
|
||||
//! Generic `ceil` algorithm.
|
||||
//!
|
||||
//! Note that this uses the algorithm from musl's `ceilf` rather than `ceil` or `ceill` 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 ceil<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_positive() {
|
||||
ix += m;
|
||||
}
|
||||
ix &= !m;
|
||||
} 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;
|
||||
} else if ix << 1 != zero {
|
||||
// 0.0 < x < 1.0; rounding up goes toward +1.0.
|
||||
return F::ONE;
|
||||
}
|
||||
}
|
||||
|
||||
F::from_bits(ix)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
|
||||
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!(ceil(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!(ceil(1.1f32), 2.0);
|
||||
assert_eq!(ceil(2.9f32), 3.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f32() {
|
||||
spec_test::<f32>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_f64() {
|
||||
assert_eq!(ceil(1.1f64), 2.0);
|
||||
assert_eq!(ceil(2.9f64), 3.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f64() {
|
||||
spec_test::<f64>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f128_enabled)]
|
||||
fn spec_tests_f128() {
|
||||
spec_test::<f128>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
mod ceil;
|
||||
mod copysign;
|
||||
mod fabs;
|
||||
mod fdim;
|
||||
mod sqrt;
|
||||
mod trunc;
|
||||
|
||||
pub use ceil::ceil;
|
||||
pub use copysign::copysign;
|
||||
pub use fabs::fabs;
|
||||
pub use fdim::fdim;
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ where
|
|||
ix = scaled.to_bits();
|
||||
match top {
|
||||
Exp::Shifted(ref mut v) => {
|
||||
*v = scaled.exp().unsigned();
|
||||
*v = scaled.exp();
|
||||
*v = (*v).wrapping_sub(F::SIG_BITS);
|
||||
}
|
||||
Exp::NoShift(()) => {
|
||||
|
|
|
|||
|
|
@ -341,12 +341,14 @@ pub use self::truncf::truncf;
|
|||
|
||||
cfg_if! {
|
||||
if #[cfg(f16_enabled)] {
|
||||
mod ceilf16;
|
||||
mod copysignf16;
|
||||
mod fabsf16;
|
||||
mod fdimf16;
|
||||
mod sqrtf16;
|
||||
mod truncf16;
|
||||
|
||||
pub use self::ceilf16::ceilf16;
|
||||
pub use self::copysignf16::copysignf16;
|
||||
pub use self::fabsf16::fabsf16;
|
||||
pub use self::fdimf16::fdimf16;
|
||||
|
|
@ -357,12 +359,14 @@ cfg_if! {
|
|||
|
||||
cfg_if! {
|
||||
if #[cfg(f128_enabled)] {
|
||||
mod ceilf128;
|
||||
mod copysignf128;
|
||||
mod fabsf128;
|
||||
mod fdimf128;
|
||||
mod sqrtf128;
|
||||
mod truncf128;
|
||||
|
||||
pub use self::ceilf128::ceilf128;
|
||||
pub use self::copysignf128::copysignf128;
|
||||
pub use self::fabsf128::fabsf128;
|
||||
pub use self::fdimf128::fdimf128;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use core::{fmt, mem, ops};
|
||||
|
||||
use super::int_traits::{CastFrom, CastInto, Int, MinInt};
|
||||
use super::int_traits::{CastFrom, Int, MinInt};
|
||||
|
||||
/// Trait for some basic operations on floats
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -34,6 +34,7 @@ pub trait Float:
|
|||
const NAN: Self;
|
||||
const MAX: Self;
|
||||
const MIN: Self;
|
||||
const EPSILON: Self;
|
||||
const PI: Self;
|
||||
const NEG_PI: Self;
|
||||
const FRAC_PI_2: Self;
|
||||
|
|
@ -107,13 +108,13 @@ pub trait Float:
|
|||
}
|
||||
|
||||
/// Returns the exponent, not adjusting for bias, not accounting for subnormals or zero.
|
||||
fn exp(self) -> i32 {
|
||||
((self.to_bits() & Self::EXP_MASK) >> Self::SIG_BITS).cast()
|
||||
fn exp(self) -> u32 {
|
||||
u32::cast_from(self.to_bits() >> Self::SIG_BITS) & Self::EXP_MAX
|
||||
}
|
||||
|
||||
/// Extract the exponent and adjust it for bias, not accounting for subnormals or zero.
|
||||
fn exp_unbiased(self) -> i32 {
|
||||
self.exp() - (Self::EXP_BIAS as i32)
|
||||
self.exp().signed() - (Self::EXP_BIAS as i32)
|
||||
}
|
||||
|
||||
/// Returns the significand with no implicit bit (or the "fractional" part)
|
||||
|
|
@ -180,6 +181,7 @@ macro_rules! float_impl {
|
|||
const MAX: Self = -Self::MIN;
|
||||
// Sign bit set, saturated mantissa, saturated exponent with last bit zeroed
|
||||
const MIN: Self = $from_bits(Self::Int::MAX & !(1 << Self::SIG_BITS));
|
||||
const EPSILON: Self = <$ty>::EPSILON;
|
||||
|
||||
const PI: Self = core::$ty::consts::PI;
|
||||
const NEG_PI: Self = -Self::PI;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue