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

Commit 1266321

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

File tree

4 files changed

+234
-37
lines changed

4 files changed

+234
-37
lines changed

src/math/generic/ceil.rs

+79-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, Round, Status};
1011
use super::super::{Float, Int, IntTy, MinInt};
1112

1213
pub fn ceil<F: Float>(x: F) -> F {
14+
ceil_round(x, Round::Nearest).val
15+
}
16+
17+
pub fn ceil_round<F: Float>(x: F, _round: Round) -> 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,33 @@ 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+
// Rounding mode has no effect
79+
for round in Round::ALL {
80+
for x in roundtrip {
81+
let FpResult { val, status } = ceil_round(x, round);
82+
assert_biteq!(val, x, "{}", Hexf(x));
83+
assert_eq!(status, Status::OK, "{}", Hexf(x));
84+
}
85+
86+
for &(x, res, res_stat) in cases {
87+
let FpResult { val, status } = ceil_round(x, round);
88+
assert_biteq!(val, res, "{}", Hexf(x));
89+
assert_eq!(status, res_stat, "{}", Hexf(x));
90+
}
6791
}
6892
}
6993

@@ -72,7 +96,17 @@ mod tests {
7296
#[test]
7397
#[cfg(f16_enabled)]
7498
fn spec_tests_f16() {
75-
spec_test::<f16>();
99+
let cases = [
100+
(0.1, 1.0, Status::INEXACT),
101+
(-0.1, -0.0, Status::INEXACT),
102+
(0.9, 1.0, Status::INEXACT),
103+
(-0.9, -0.0, Status::INEXACT),
104+
(1.1, 2.0, Status::INEXACT),
105+
(-1.1, -1.0, Status::INEXACT),
106+
(1.9, 2.0, Status::INEXACT),
107+
(-1.9, -1.0, Status::INEXACT),
108+
];
109+
spec_test::<f16>(&cases);
76110
}
77111

78112
#[test]
@@ -83,7 +117,17 @@ mod tests {
83117

84118
#[test]
85119
fn spec_tests_f32() {
86-
spec_test::<f32>();
120+
let cases = [
121+
(0.1, 1.0, Status::INEXACT),
122+
(-0.1, -0.0, Status::INEXACT),
123+
(0.9, 1.0, Status::INEXACT),
124+
(-0.9, -0.0, Status::INEXACT),
125+
(1.1, 2.0, Status::INEXACT),
126+
(-1.1, -1.0, Status::INEXACT),
127+
(1.9, 2.0, Status::INEXACT),
128+
(-1.9, -1.0, Status::INEXACT),
129+
];
130+
spec_test::<f32>(&cases);
87131
}
88132

89133
#[test]
@@ -94,12 +138,32 @@ mod tests {
94138

95139
#[test]
96140
fn spec_tests_f64() {
97-
spec_test::<f64>();
141+
let cases = [
142+
(0.1, 1.0, Status::INEXACT),
143+
(-0.1, -0.0, Status::INEXACT),
144+
(0.9, 1.0, Status::INEXACT),
145+
(-0.9, -0.0, Status::INEXACT),
146+
(1.1, 2.0, Status::INEXACT),
147+
(-1.1, -1.0, Status::INEXACT),
148+
(1.9, 2.0, Status::INEXACT),
149+
(-1.9, -1.0, Status::INEXACT),
150+
];
151+
spec_test::<f64>(&cases);
98152
}
99153

100154
#[test]
101155
#[cfg(f128_enabled)]
102156
fn spec_tests_f128() {
103-
spec_test::<f128>();
157+
let cases = [
158+
(0.1, 1.0, Status::INEXACT),
159+
(-0.1, -0.0, Status::INEXACT),
160+
(0.9, 1.0, Status::INEXACT),
161+
(-0.9, -0.0, Status::INEXACT),
162+
(1.1, 2.0, Status::INEXACT),
163+
(-1.1, -1.0, Status::INEXACT),
164+
(1.9, 2.0, Status::INEXACT),
165+
(-1.9, -1.0, Status::INEXACT),
166+
];
167+
spec_test::<f128>(&cases);
104168
}
105169
}

src/math/generic/floor.rs

+63-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, Round, Status};
1011
use super::super::{Float, Int, IntTy, MinInt};
1112

1213
pub fn floor<F: Float>(x: F) -> F {
14+
floor_round(x, Round::Nearest).val
15+
}
16+
17+
pub fn floor_round<F: Float>(x: F, _round: Round) -> 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,43 @@ 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+
// Rounding mode has no effect
79+
for round in Round::ALL {
80+
for x in roundtrip {
81+
let FpResult { val, status } = floor_round(x, round);
82+
assert_biteq!(val, x, "{}", Hexf(x));
83+
assert_eq!(status, Status::OK, "{}", Hexf(x));
84+
}
85+
86+
for &(x, res, res_stat) in cases {
87+
let FpResult { val, status } = floor_round(x, round);
88+
assert_biteq!(val, res, "{}", Hexf(x));
89+
assert_eq!(status, res_stat, "{}", Hexf(x));
90+
}
6791
}
6892
}
6993

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

7296
#[test]
7397
#[cfg(f16_enabled)]
7498
fn spec_tests_f16() {
75-
spec_test::<f16>();
99+
let cases = [];
100+
spec_test::<f16>(&cases);
76101
}
77102

78103
#[test]
@@ -84,7 +109,17 @@ mod tests {
84109

85110
#[test]
86111
fn spec_tests_f32() {
87-
spec_test::<f32>();
112+
let cases = [
113+
(0.1, 0.0, Status::INEXACT),
114+
(-0.1, -1.0, Status::INEXACT),
115+
(0.9, 0.0, Status::INEXACT),
116+
(-0.9, -1.0, Status::INEXACT),
117+
(1.1, 1.0, Status::INEXACT),
118+
(-1.1, -2.0, Status::INEXACT),
119+
(1.9, 1.0, Status::INEXACT),
120+
(-1.9, -2.0, Status::INEXACT),
121+
];
122+
spec_test::<f32>(&cases);
88123
}
89124

90125
#[test]
@@ -95,12 +130,23 @@ mod tests {
95130

96131
#[test]
97132
fn spec_tests_f64() {
98-
spec_test::<f64>();
133+
let cases = [
134+
(0.1, 0.0, Status::INEXACT),
135+
(-0.1, -1.0, Status::INEXACT),
136+
(0.9, 0.0, Status::INEXACT),
137+
(-0.9, -1.0, Status::INEXACT),
138+
(1.1, 1.0, Status::INEXACT),
139+
(-1.1, -2.0, Status::INEXACT),
140+
(1.9, 1.0, Status::INEXACT),
141+
(-1.9, -2.0, Status::INEXACT),
142+
];
143+
spec_test::<f64>(&cases);
99144
}
100145

101146
#[test]
102147
#[cfg(f128_enabled)]
103148
fn spec_tests_f128() {
104-
spec_test::<f128>();
149+
let cases = [];
150+
spec_test::<f128>(&cases);
105151
}
106152
}

0 commit comments

Comments
 (0)