Skip to content

Commit 812e15a

Browse files
authored
Merge pull request #436 from tgross35/generic-ceil
Add `ceilf16` and `ceilf128`
2 parents 54b6e57 + a836f6e commit 812e15a

18 files changed

+168
-102
lines changed

crates/libm-macros/src/shared.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
99
FloatTy::F16,
1010
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
1111
None,
12-
&["fabsf16", "sqrtf16", "truncf16"],
12+
&["ceilf16", "fabsf16", "sqrtf16", "truncf16"],
1313
),
1414
(
1515
// `fn(f32) -> f32`
@@ -40,7 +40,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
4040
FloatTy::F128,
4141
Signature { args: &[Ty::F128], returns: &[Ty::F128] },
4242
None,
43-
&["fabsf128", "sqrtf128", "truncf128"],
43+
&["ceilf128", "fabsf128", "sqrtf128", "truncf128"],
4444
),
4545
(
4646
// `(f16, f16) -> f16`

crates/libm-test/benches/icount.rs

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ main!(
6969
icount_bench_cbrt_group,
7070
icount_bench_cbrtf_group,
7171
icount_bench_ceil_group,
72+
icount_bench_ceilf128_group,
73+
icount_bench_ceilf16_group,
7274
icount_bench_ceilf_group,
7375
icount_bench_copysign_group,
7476
icount_bench_copysignf128_group,

crates/libm-test/benches/random.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ libm_macros::for_each_function! {
117117
exp10 | exp10f | exp2 | exp2f => (true, Some(musl_math_sys::MACRO_FN_NAME)),
118118

119119
// Musl does not provide `f16` and `f128` functions
120-
copysignf128
120+
ceilf128
121+
| ceilf16
122+
| copysignf128
121123
| copysignf16
122124
| fabsf128
123125
| fabsf16

crates/libm-test/src/f8_impl.rs

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ impl Float for f8 {
3030
const INFINITY: Self = Self(0b0_1111_000);
3131
const NEG_INFINITY: Self = Self(0b1_1111_000);
3232
const NAN: Self = Self(0b0_1111_100);
33+
// FIXME: incorrect values
34+
const EPSILON: Self = Self::ZERO;
3335
const PI: Self = Self::ZERO;
3436
const NEG_PI: Self = Self::ZERO;
3537
const FRAC_PI_2: Self = Self::ZERO;

crates/libm-test/src/mpfloat.rs

+4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ libm_macros::for_each_function! {
137137
// Most of these need a manual implementation
138138
ceil,
139139
ceilf,
140+
ceilf128,
141+
ceilf16,
140142
copysign,
141143
copysignf,
142144
copysignf128,
@@ -237,12 +239,14 @@ impl_no_round! {
237239
#[cfg(f16_enabled)]
238240
impl_no_round! {
239241
fabsf16 => abs_mut;
242+
ceilf16 => ceil_mut;
240243
truncf16 => trunc_mut;
241244
}
242245

243246
#[cfg(f128_enabled)]
244247
impl_no_round! {
245248
fabsf128 => abs_mut;
249+
ceilf128 => ceil_mut;
246250
truncf128 => trunc_mut;
247251
}
248252

crates/libm-test/tests/compare_built_musl.rs

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ libm_macros::for_each_function! {
7979
ynf,
8080

8181
// Not provided by musl
82+
ceilf128,
83+
ceilf16,
8284
copysignf128,
8385
copysignf16,
8486
fabsf128,

crates/util/src/main.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
8484
emit_types: [CFn, RustFn, RustArgs],
8585
extra: (basis, op, inputs),
8686
fn_extra: match MACRO_FN_NAME {
87-
copysignf128
87+
ceilf128
88+
| ceilf16
89+
| copysignf128
8890
| copysignf16
8991
| fabsf128
9092
| fabsf16

etc/function-definitions.json

+18-2
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,33 @@
109109
"src/libm_helper.rs",
110110
"src/math/arch/i586.rs",
111111
"src/math/arch/wasm32.rs",
112-
"src/math/ceil.rs"
112+
"src/math/ceil.rs",
113+
"src/math/generic/ceil.rs"
113114
],
114115
"type": "f64"
115116
},
116117
"ceilf": {
117118
"sources": [
118119
"src/math/arch/wasm32.rs",
119-
"src/math/ceilf.rs"
120+
"src/math/ceilf.rs",
121+
"src/math/generic/ceil.rs"
120122
],
121123
"type": "f32"
122124
},
125+
"ceilf128": {
126+
"sources": [
127+
"src/math/ceilf128.rs",
128+
"src/math/generic/ceil.rs"
129+
],
130+
"type": "f128"
131+
},
132+
"ceilf16": {
133+
"sources": [
134+
"src/math/ceilf16.rs",
135+
"src/math/generic/ceil.rs"
136+
],
137+
"type": "f16"
138+
},
123139
"copysign": {
124140
"sources": [
125141
"src/libm_helper.rs",

etc/function-list.txt

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ cbrt
1717
cbrtf
1818
ceil
1919
ceilf
20+
ceilf128
21+
ceilf16
2022
copysign
2123
copysignf
2224
copysignf128

src/math/ceil.rs

+1-41
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![allow(unreachable_code)]
2-
use core::f64;
3-
4-
const TOINT: f64 = 1. / f64::EPSILON;
5-
61
/// Ceil (f64)
72
///
83
/// Finds the nearest integer greater than or equal to `x`.
@@ -15,40 +10,5 @@ pub fn ceil(x: f64) -> f64 {
1510
args: x,
1611
}
1712

18-
let u: u64 = x.to_bits();
19-
let e: i64 = ((u >> 52) & 0x7ff) as i64;
20-
let y: f64;
21-
22-
if e >= 0x3ff + 52 || x == 0. {
23-
return x;
24-
}
25-
// y = int(x) - x, where int(x) is an integer neighbor of x
26-
y = if (u >> 63) != 0 { x - TOINT + TOINT - x } else { x + TOINT - TOINT - x };
27-
// special case because of non-nearest rounding modes
28-
if e < 0x3ff {
29-
force_eval!(y);
30-
return if (u >> 63) != 0 { -0. } else { 1. };
31-
}
32-
if y < 0. { x + y + 1. } else { x + y }
33-
}
34-
35-
#[cfg(test)]
36-
mod tests {
37-
use super::*;
38-
39-
#[test]
40-
fn sanity_check() {
41-
assert_eq!(ceil(1.1), 2.0);
42-
assert_eq!(ceil(2.9), 3.0);
43-
}
44-
45-
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
46-
#[test]
47-
fn spec_tests() {
48-
// Not Asserted: that the current rounding mode has no effect.
49-
assert!(ceil(f64::NAN).is_nan());
50-
for f in [0.0, -0.0, f64::INFINITY, f64::NEG_INFINITY].iter().copied() {
51-
assert_eq!(ceil(f), f);
52-
}
53-
}
13+
super::generic::ceil(x)
5414
}

src/math/ceilf.rs

+1-50
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use core::f32;
2-
31
/// Ceil (f32)
42
///
53
/// Finds the nearest integer greater than or equal to `x`.
@@ -11,52 +9,5 @@ pub fn ceilf(x: f32) -> f32 {
119
args: x,
1210
}
1311

14-
let mut ui = x.to_bits();
15-
let e = (((ui >> 23) & 0xff).wrapping_sub(0x7f)) as i32;
16-
17-
if e >= 23 {
18-
return x;
19-
}
20-
if e >= 0 {
21-
let m = 0x007fffff >> e;
22-
if (ui & m) == 0 {
23-
return x;
24-
}
25-
force_eval!(x + f32::from_bits(0x7b800000));
26-
if ui >> 31 == 0 {
27-
ui += m;
28-
}
29-
ui &= !m;
30-
} else {
31-
force_eval!(x + f32::from_bits(0x7b800000));
32-
if ui >> 31 != 0 {
33-
return -0.0;
34-
} else if ui << 1 != 0 {
35-
return 1.0;
36-
}
37-
}
38-
f32::from_bits(ui)
39-
}
40-
41-
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
42-
#[cfg(not(target_arch = "powerpc64"))]
43-
#[cfg(test)]
44-
mod tests {
45-
use super::*;
46-
47-
#[test]
48-
fn sanity_check() {
49-
assert_eq!(ceilf(1.1), 2.0);
50-
assert_eq!(ceilf(2.9), 3.0);
51-
}
52-
53-
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
54-
#[test]
55-
fn spec_tests() {
56-
// Not Asserted: that the current rounding mode has no effect.
57-
assert!(ceilf(f32::NAN).is_nan());
58-
for f in [0.0, -0.0, f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
59-
assert_eq!(ceilf(f), f);
60-
}
61-
}
12+
super::generic::ceil(x)
6213
}

src/math/ceilf128.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// Ceil (f128)
2+
///
3+
/// Finds the nearest integer greater than or equal to `x`.
4+
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
5+
pub fn ceilf128(x: f128) -> f128 {
6+
super::generic::ceil(x)
7+
}

src/math/ceilf16.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// Ceil (f16)
2+
///
3+
/// Finds the nearest integer greater than or equal to `x`.
4+
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
5+
pub fn ceilf16(x: f16) -> f16 {
6+
super::generic::ceil(x)
7+
}

src/math/generic/ceil.rs

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/* SPDX-License-Identifier: MIT */
2+
/* origin: musl src/math/ceilf.c */
3+
4+
//! Generic `ceil` algorithm.
5+
//!
6+
//! Note that this uses the algorithm from musl's `ceilf` rather than `ceil` or `ceill` because
7+
//! performance seems to be better (based on icount) and it does not seem to experience rounding
8+
//! errors on i386.
9+
10+
use super::super::{Float, Int, IntTy, MinInt};
11+
12+
pub fn ceil<F: Float>(x: F) -> F {
13+
let zero = IntTy::<F>::ZERO;
14+
15+
let mut ix = x.to_bits();
16+
let e = x.exp_unbiased();
17+
18+
// If the represented value has no fractional part, no truncation is needed.
19+
if e >= F::SIG_BITS as i32 {
20+
return x;
21+
}
22+
23+
if e >= 0 {
24+
// |x| >= 1.0
25+
26+
let m = F::SIG_MASK >> e.unsigned();
27+
if (ix & m) == zero {
28+
// Portion to be masked is already zero; no adjustment needed.
29+
return x;
30+
}
31+
32+
// Otherwise, raise an inexact exception.
33+
force_eval!(x + F::MAX);
34+
if x.is_sign_positive() {
35+
ix += m;
36+
}
37+
ix &= !m;
38+
} else {
39+
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
40+
force_eval!(x + F::MAX);
41+
42+
if x.is_sign_negative() {
43+
// -1.0 < x <= -0.0; rounding up goes toward -0.0.
44+
return F::NEG_ZERO;
45+
} else if ix << 1 != zero {
46+
// 0.0 < x < 1.0; rounding up goes toward +1.0.
47+
return F::ONE;
48+
}
49+
}
50+
51+
F::from_bits(ix)
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use super::*;
57+
58+
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
59+
fn spec_test<F: Float>() {
60+
// Not Asserted: that the current rounding mode has no effect.
61+
for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() {
62+
assert_biteq!(ceil(f), f);
63+
}
64+
}
65+
66+
/* Skipping f16 / f128 "sanity_check"s due to rejected literal lexing at MSRV */
67+
68+
#[test]
69+
#[cfg(f16_enabled)]
70+
fn spec_tests_f16() {
71+
spec_test::<f16>();
72+
}
73+
74+
#[test]
75+
fn sanity_check_f32() {
76+
assert_eq!(ceil(1.1f32), 2.0);
77+
assert_eq!(ceil(2.9f32), 3.0);
78+
}
79+
80+
#[test]
81+
fn spec_tests_f32() {
82+
spec_test::<f32>();
83+
}
84+
85+
#[test]
86+
fn sanity_check_f64() {
87+
assert_eq!(ceil(1.1f64), 2.0);
88+
assert_eq!(ceil(2.9f64), 3.0);
89+
}
90+
91+
#[test]
92+
fn spec_tests_f64() {
93+
spec_test::<f64>();
94+
}
95+
96+
#[test]
97+
#[cfg(f128_enabled)]
98+
fn spec_tests_f128() {
99+
spec_test::<f128>();
100+
}
101+
}

src/math/generic/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
mod ceil;
12
mod copysign;
23
mod fabs;
34
mod fdim;
45
mod sqrt;
56
mod trunc;
67

8+
pub use ceil::ceil;
79
pub use copysign::copysign;
810
pub use fabs::fabs;
911
pub use fdim::fdim;

src/math/generic/sqrt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ where
9696
ix = scaled.to_bits();
9797
match top {
9898
Exp::Shifted(ref mut v) => {
99-
*v = scaled.exp().unsigned();
99+
*v = scaled.exp();
100100
*v = (*v).wrapping_sub(F::SIG_BITS);
101101
}
102102
Exp::NoShift(()) => {

0 commit comments

Comments
 (0)