Skip to content

Commit 51ff583

Browse files
committed
Auto merge of #95317 - Jules-Bertholet:round_ties_to_even, r=pnkfelix,m-ou-se,scottmcm
Add `round_ties_even` to `f32` and `f64` Tracking issue: #96710 Redux of #82273. See also #55107 Adds a new method, `round_ties_even`, to `f32` and `f64`, that rounds the float to the nearest integer , rounding halfway cases to the number with an even least significant bit. Uses the `roundeven` LLVM intrinsic to do this. Of the five IEEE 754 rounding modes, this is the only one that doesn't already have a round-to-integer function exposed by Rust (others are `round`, `floor`, `ceil`, and `trunc`). Ties-to-even is also the rounding mode used for int-to-float and float-to-float `as` casts, as well as float arithmentic operations. So not having an explicit rounding method for it seems like an oversight. Bikeshed: this PR currently uses `round_ties_even` for the name of the method. But maybe `round_ties_to_even` is better, or `round_even`, or `round_to_even`?
2 parents 3528152 + f41c30a commit 51ff583

File tree

6 files changed

+112
-0
lines changed

6 files changed

+112
-0
lines changed

core/src/intrinsics.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,9 +1585,15 @@ extern "rust-intrinsic" {
15851585

15861586
/// Returns the nearest integer to an `f32`. May raise an inexact floating-point exception
15871587
/// if the argument is not an integer.
1588+
///
1589+
/// The stabilized version of this intrinsic is
1590+
/// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even)
15881591
pub fn rintf32(x: f32) -> f32;
15891592
/// Returns the nearest integer to an `f64`. May raise an inexact floating-point exception
15901593
/// if the argument is not an integer.
1594+
///
1595+
/// The stabilized version of this intrinsic is
1596+
/// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even)
15911597
pub fn rintf64(x: f64) -> f64;
15921598

15931599
/// Returns the nearest integer to an `f32`.
@@ -1610,6 +1616,19 @@ extern "rust-intrinsic" {
16101616
/// [`f64::round`](../../std/primitive.f64.html#method.round)
16111617
pub fn roundf64(x: f64) -> f64;
16121618

1619+
/// Returns the nearest integer to an `f32`. Rounds half-way cases to the number
1620+
/// with an even least significant digit.
1621+
///
1622+
/// This intrinsic does not have a stable counterpart.
1623+
#[cfg(not(bootstrap))]
1624+
pub fn roundevenf32(x: f32) -> f32;
1625+
/// Returns the nearest integer to an `f64`. Rounds half-way cases to the number
1626+
/// with an even least significant digit.
1627+
///
1628+
/// This intrinsic does not have a stable counterpart.
1629+
#[cfg(not(bootstrap))]
1630+
pub fn roundevenf64(x: f64) -> f64;
1631+
16131632
/// Float addition that allows optimizations based on algebraic rules.
16141633
/// May assume inputs are finite.
16151634
///

std/src/f32.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,14 @@ impl f32 {
7878
/// let f = 3.3_f32;
7979
/// let g = -3.3_f32;
8080
/// let h = -3.7_f32;
81+
/// let i = 3.5_f32;
82+
/// let j = 4.5_f32;
8183
///
8284
/// assert_eq!(f.round(), 3.0);
8385
/// assert_eq!(g.round(), -3.0);
8486
/// assert_eq!(h.round(), -4.0);
87+
/// assert_eq!(i.round(), 4.0);
88+
/// assert_eq!(j.round(), 5.0);
8589
/// ```
8690
#[rustc_allow_incoherent_impl]
8791
#[must_use = "method returns a new number and does not mutate the original value"]
@@ -91,6 +95,32 @@ impl f32 {
9195
unsafe { intrinsics::roundf32(self) }
9296
}
9397

98+
/// Returns the nearest integer to a number. Rounds half-way cases to the number
99+
/// with an even least significant digit.
100+
///
101+
/// # Examples
102+
///
103+
/// ```
104+
/// #![feature(round_ties_even)]
105+
///
106+
/// let f = 3.3_f32;
107+
/// let g = -3.3_f32;
108+
/// let h = 3.5_f32;
109+
/// let i = 4.5_f32;
110+
///
111+
/// assert_eq!(f.round_ties_even(), 3.0);
112+
/// assert_eq!(g.round_ties_even(), -3.0);
113+
/// assert_eq!(h.round_ties_even(), 4.0);
114+
/// assert_eq!(i.round_ties_even(), 4.0);
115+
/// ```
116+
#[rustc_allow_incoherent_impl]
117+
#[must_use = "method returns a new number and does not mutate the original value"]
118+
#[unstable(feature = "round_ties_even", issue = "96710")]
119+
#[inline]
120+
pub fn round_ties_even(self) -> f32 {
121+
unsafe { intrinsics::rintf32(self) }
122+
}
123+
94124
/// Returns the integer part of `self`.
95125
/// This means that non-integer numbers are always truncated towards zero.
96126
///

std/src/f32/tests.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ fn test_ceil() {
209209

210210
#[test]
211211
fn test_round() {
212+
assert_approx_eq!(2.5f32.round(), 3.0f32);
212213
assert_approx_eq!(1.0f32.round(), 1.0f32);
213214
assert_approx_eq!(1.3f32.round(), 1.0f32);
214215
assert_approx_eq!(1.5f32.round(), 2.0f32);
@@ -221,6 +222,21 @@ fn test_round() {
221222
assert_approx_eq!((-1.7f32).round(), -2.0f32);
222223
}
223224

225+
#[test]
226+
fn test_round_ties_even() {
227+
assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32);
228+
assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32);
229+
assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32);
230+
assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32);
231+
assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32);
232+
assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32);
233+
assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32);
234+
assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32);
235+
assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32);
236+
assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32);
237+
assert_approx_eq!((-1.7f32).round_ties_even(), -2.0f32);
238+
}
239+
224240
#[test]
225241
fn test_trunc() {
226242
assert_approx_eq!(1.0f32.trunc(), 1.0f32);

std/src/f64.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,14 @@ impl f64 {
7878
/// let f = 3.3_f64;
7979
/// let g = -3.3_f64;
8080
/// let h = -3.7_f64;
81+
/// let i = 3.5_f64;
82+
/// let j = 4.5_f64;
8183
///
8284
/// assert_eq!(f.round(), 3.0);
8385
/// assert_eq!(g.round(), -3.0);
8486
/// assert_eq!(h.round(), -4.0);
87+
/// assert_eq!(i.round(), 4.0);
88+
/// assert_eq!(j.round(), 5.0);
8589
/// ```
8690
#[rustc_allow_incoherent_impl]
8791
#[must_use = "method returns a new number and does not mutate the original value"]
@@ -91,6 +95,32 @@ impl f64 {
9195
unsafe { intrinsics::roundf64(self) }
9296
}
9397

98+
/// Returns the nearest integer to a number. Rounds half-way cases to the number
99+
/// with an even least significant digit.
100+
///
101+
/// # Examples
102+
///
103+
/// ```
104+
/// #![feature(round_ties_even)]
105+
///
106+
/// let f = 3.3_f64;
107+
/// let g = -3.3_f64;
108+
/// let h = 3.5_f64;
109+
/// let i = 4.5_f64;
110+
///
111+
/// assert_eq!(f.round_ties_even(), 3.0);
112+
/// assert_eq!(g.round_ties_even(), -3.0);
113+
/// assert_eq!(h.round_ties_even(), 4.0);
114+
/// assert_eq!(i.round_ties_even(), 4.0);
115+
/// ```
116+
#[rustc_allow_incoherent_impl]
117+
#[must_use = "method returns a new number and does not mutate the original value"]
118+
#[unstable(feature = "round_ties_even", issue = "96710")]
119+
#[inline]
120+
pub fn round_ties_even(self) -> f64 {
121+
unsafe { intrinsics::rintf64(self) }
122+
}
123+
94124
/// Returns the integer part of `self`.
95125
/// This means that non-integer numbers are always truncated towards zero.
96126
///

std/src/f64/tests.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ fn test_ceil() {
199199

200200
#[test]
201201
fn test_round() {
202+
assert_approx_eq!(2.5f64.round(), 3.0f64);
202203
assert_approx_eq!(1.0f64.round(), 1.0f64);
203204
assert_approx_eq!(1.3f64.round(), 1.0f64);
204205
assert_approx_eq!(1.5f64.round(), 2.0f64);
@@ -211,6 +212,21 @@ fn test_round() {
211212
assert_approx_eq!((-1.7f64).round(), -2.0f64);
212213
}
213214

215+
#[test]
216+
fn test_round_ties_even() {
217+
assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64);
218+
assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64);
219+
assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64);
220+
assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64);
221+
assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64);
222+
assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64);
223+
assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64);
224+
assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64);
225+
assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64);
226+
assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64);
227+
assert_approx_eq!((-1.7f64).round_ties_even(), -2.0f64);
228+
}
229+
214230
#[test]
215231
fn test_trunc() {
216232
assert_approx_eq!(1.0f64.trunc(), 1.0f64);

std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@
304304
#![feature(provide_any)]
305305
#![feature(ptr_as_uninit)]
306306
#![feature(raw_os_nonzero)]
307+
#![feature(round_ties_even)]
307308
#![feature(slice_internals)]
308309
#![feature(slice_ptr_get)]
309310
#![feature(std_internals)]

0 commit comments

Comments
 (0)