Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit b815982

Browse files
committed
Eliminate the use of force_eval! in ceil, floor, and trunc
1 parent d197af6 commit b815982

File tree

3 files changed

+220
-37
lines changed

3 files changed

+220
-37
lines changed

libm/src/math/generic/ceil.rs

+76-15
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,35 @@
77
//! performance seems to be better (based on icount) and it does not seem to experience rounding
88
//! errors on i386.
99
10+
use super::super::support::{FpResult, Status};
1011
use super::super::{Float, Int, IntTy, MinInt};
1112

1213
pub fn ceil<F: Float>(x: F) -> F {
14+
ceil_status(x).val
15+
}
16+
17+
pub fn ceil_status<F: Float>(x: F) -> FpResult<F> {
1318
let zero = IntTy::<F>::ZERO;
1419

1520
let mut ix = x.to_bits();
1621
let e = x.exp_unbiased();
1722

1823
// If the represented value has no fractional part, no truncation is needed.
1924
if e >= F::SIG_BITS as i32 {
20-
return x;
25+
return FpResult::ok(x);
2126
}
2227

23-
if e >= 0 {
28+
let status;
29+
let res = if e >= 0 {
2430
// |x| >= 1.0
25-
2631
let m = F::SIG_MASK >> e.unsigned();
2732
if (ix & m) == zero {
2833
// Portion to be masked is already zero; no adjustment needed.
29-
return x;
34+
return FpResult::ok(x);
3035
}
3136

3237
// Otherwise, raise an inexact exception.
33-
force_eval!(x + F::MAX);
38+
status = Status::INEXACT;
3439

3540
if x.is_sign_positive() {
3641
ix += m;
@@ -40,7 +45,11 @@ pub fn ceil<F: Float>(x: F) -> F {
4045
F::from_bits(ix)
4146
} else {
4247
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
43-
force_eval!(x + F::MAX);
48+
if ix & F::SIG_MASK == F::Int::ZERO {
49+
status = Status::OK;
50+
} else {
51+
status = Status::INEXACT;
52+
}
4453

4554
if x.is_sign_negative() {
4655
// -1.0 < x <= -0.0; rounding up goes toward -0.0.
@@ -52,18 +61,30 @@ pub fn ceil<F: Float>(x: F) -> F {
5261
// +0.0 remains unchanged
5362
x
5463
}
55-
}
64+
};
65+
66+
FpResult::new(res, status)
5667
}
5768

5869
#[cfg(test)]
5970
mod tests {
6071
use super::*;
72+
use crate::support::Hexf;
6173

6274
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
63-
fn spec_test<F: Float>() {
64-
// Not Asserted: that the current rounding mode has no effect.
65-
for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() {
66-
assert_biteq!(ceil(f), f);
75+
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
76+
let roundtrip = [F::ZERO, F::ONE, F::NEG_ONE, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY];
77+
78+
for x in roundtrip {
79+
let FpResult { val, status } = ceil_status(x);
80+
assert_biteq!(val, x, "{}", Hexf(x));
81+
assert_eq!(status, Status::OK, "{}", Hexf(x));
82+
}
83+
84+
for &(x, res, res_stat) in cases {
85+
let FpResult { val, status } = ceil_status(x);
86+
assert_biteq!(val, res, "{}", Hexf(x));
87+
assert_eq!(status, res_stat, "{}", Hexf(x));
6788
}
6889
}
6990

@@ -72,7 +93,17 @@ mod tests {
7293
#[test]
7394
#[cfg(f16_enabled)]
7495
fn spec_tests_f16() {
75-
spec_test::<f16>();
96+
let cases = [
97+
(0.1, 1.0, Status::INEXACT),
98+
(-0.1, -0.0, Status::INEXACT),
99+
(0.9, 1.0, Status::INEXACT),
100+
(-0.9, -0.0, Status::INEXACT),
101+
(1.1, 2.0, Status::INEXACT),
102+
(-1.1, -1.0, Status::INEXACT),
103+
(1.9, 2.0, Status::INEXACT),
104+
(-1.9, -1.0, Status::INEXACT),
105+
];
106+
spec_test::<f16>(&cases);
76107
}
77108

78109
#[test]
@@ -83,7 +114,17 @@ mod tests {
83114

84115
#[test]
85116
fn spec_tests_f32() {
86-
spec_test::<f32>();
117+
let cases = [
118+
(0.1, 1.0, Status::INEXACT),
119+
(-0.1, -0.0, Status::INEXACT),
120+
(0.9, 1.0, Status::INEXACT),
121+
(-0.9, -0.0, Status::INEXACT),
122+
(1.1, 2.0, Status::INEXACT),
123+
(-1.1, -1.0, Status::INEXACT),
124+
(1.9, 2.0, Status::INEXACT),
125+
(-1.9, -1.0, Status::INEXACT),
126+
];
127+
spec_test::<f32>(&cases);
87128
}
88129

89130
#[test]
@@ -94,12 +135,32 @@ mod tests {
94135

95136
#[test]
96137
fn spec_tests_f64() {
97-
spec_test::<f64>();
138+
let cases = [
139+
(0.1, 1.0, Status::INEXACT),
140+
(-0.1, -0.0, Status::INEXACT),
141+
(0.9, 1.0, Status::INEXACT),
142+
(-0.9, -0.0, Status::INEXACT),
143+
(1.1, 2.0, Status::INEXACT),
144+
(-1.1, -1.0, Status::INEXACT),
145+
(1.9, 2.0, Status::INEXACT),
146+
(-1.9, -1.0, Status::INEXACT),
147+
];
148+
spec_test::<f64>(&cases);
98149
}
99150

100151
#[test]
101152
#[cfg(f128_enabled)]
102153
fn spec_tests_f128() {
103-
spec_test::<f128>();
154+
let cases = [
155+
(0.1, 1.0, Status::INEXACT),
156+
(-0.1, -0.0, Status::INEXACT),
157+
(0.9, 1.0, Status::INEXACT),
158+
(-0.9, -0.0, Status::INEXACT),
159+
(1.1, 2.0, Status::INEXACT),
160+
(-1.1, -1.0, Status::INEXACT),
161+
(1.9, 2.0, Status::INEXACT),
162+
(-1.9, -1.0, Status::INEXACT),
163+
];
164+
spec_test::<f128>(&cases);
104165
}
105166
}

libm/src/math/generic/floor.rs

+60-17
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,35 @@
77
//! performance seems to be better (based on icount) and it does not seem to experience rounding
88
//! errors on i386.
99
10+
use super::super::support::{FpResult, Status};
1011
use super::super::{Float, Int, IntTy, MinInt};
1112

1213
pub fn floor<F: Float>(x: F) -> F {
14+
floor_status(x).val
15+
}
16+
17+
pub fn floor_status<F: Float>(x: F) -> FpResult<F> {
1318
let zero = IntTy::<F>::ZERO;
1419

1520
let mut ix = x.to_bits();
1621
let e = x.exp_unbiased();
1722

1823
// If the represented value has no fractional part, no truncation is needed.
1924
if e >= F::SIG_BITS as i32 {
20-
return x;
25+
return FpResult::ok(x);
2126
}
2227

23-
if e >= 0 {
28+
let status;
29+
let res = if e >= 0 {
2430
// |x| >= 1.0
25-
2631
let m = F::SIG_MASK >> e.unsigned();
2732
if ix & m == zero {
2833
// Portion to be masked is already zero; no adjustment needed.
29-
return x;
34+
return FpResult::ok(x);
3035
}
3136

3237
// Otherwise, raise an inexact exception.
33-
force_eval!(x + F::MAX);
38+
status = Status::INEXACT;
3439

3540
if x.is_sign_negative() {
3641
ix += m;
@@ -39,8 +44,12 @@ pub fn floor<F: Float>(x: F) -> F {
3944
ix &= !m;
4045
F::from_bits(ix)
4146
} else {
42-
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
43-
force_eval!(x + F::MAX);
47+
// |x| < 1.0, raise an inexact exception since truncation will happen.
48+
if ix & F::SIG_MASK == F::Int::ZERO {
49+
status = Status::OK;
50+
} else {
51+
status = Status::INEXACT;
52+
}
4453

4554
if x.is_sign_positive() {
4655
// 0.0 <= x < 1.0; rounding down goes toward +0.0.
@@ -52,27 +61,40 @@ pub fn floor<F: Float>(x: F) -> F {
5261
// -0.0 remains unchanged
5362
x
5463
}
55-
}
64+
};
65+
66+
FpResult::new(res, status)
5667
}
5768

5869
#[cfg(test)]
5970
mod tests {
6071
use super::*;
72+
use crate::support::Hexf;
6173

6274
/// Test against https://en.cppreference.com/w/cpp/numeric/math/floor
63-
fn spec_test<F: Float>() {
64-
// Not Asserted: that the current rounding mode has no effect.
65-
for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() {
66-
assert_biteq!(floor(f), f);
75+
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
76+
let roundtrip = [F::ZERO, F::ONE, F::NEG_ONE, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY];
77+
78+
for x in roundtrip {
79+
let FpResult { val, status } = floor_status(x);
80+
assert_biteq!(val, x, "{}", Hexf(x));
81+
assert_eq!(status, Status::OK, "{}", Hexf(x));
82+
}
83+
84+
for &(x, res, res_stat) in cases {
85+
let FpResult { val, status } = floor_status(x);
86+
assert_biteq!(val, res, "{}", Hexf(x));
87+
assert_eq!(status, res_stat, "{}", Hexf(x));
6788
}
6889
}
6990

70-
/* Skipping f16 / f128 "sanity_check"s due to rejected literal lexing at MSRV */
91+
/* Skipping f16 / f128 "sanity_check"s and spec cases due to rejected literal lexing at MSRV */
7192

7293
#[test]
7394
#[cfg(f16_enabled)]
7495
fn spec_tests_f16() {
75-
spec_test::<f16>();
96+
let cases = [];
97+
spec_test::<f16>(&cases);
7698
}
7799

78100
#[test]
@@ -84,7 +106,17 @@ mod tests {
84106

85107
#[test]
86108
fn spec_tests_f32() {
87-
spec_test::<f32>();
109+
let cases = [
110+
(0.1, 0.0, Status::INEXACT),
111+
(-0.1, -1.0, Status::INEXACT),
112+
(0.9, 0.0, Status::INEXACT),
113+
(-0.9, -1.0, Status::INEXACT),
114+
(1.1, 1.0, Status::INEXACT),
115+
(-1.1, -2.0, Status::INEXACT),
116+
(1.9, 1.0, Status::INEXACT),
117+
(-1.9, -2.0, Status::INEXACT),
118+
];
119+
spec_test::<f32>(&cases);
88120
}
89121

90122
#[test]
@@ -95,12 +127,23 @@ mod tests {
95127

96128
#[test]
97129
fn spec_tests_f64() {
98-
spec_test::<f64>();
130+
let cases = [
131+
(0.1, 0.0, Status::INEXACT),
132+
(-0.1, -1.0, Status::INEXACT),
133+
(0.9, 0.0, Status::INEXACT),
134+
(-0.9, -1.0, Status::INEXACT),
135+
(1.1, 1.0, Status::INEXACT),
136+
(-1.1, -2.0, Status::INEXACT),
137+
(1.9, 1.0, Status::INEXACT),
138+
(-1.9, -2.0, Status::INEXACT),
139+
];
140+
spec_test::<f64>(&cases);
99141
}
100142

101143
#[test]
102144
#[cfg(f128_enabled)]
103145
fn spec_tests_f128() {
104-
spec_test::<f128>();
146+
let cases = [];
147+
spec_test::<f128>(&cases);
105148
}
106149
}

0 commit comments

Comments
 (0)