about summary refs log tree commit diff
path: root/library
diff options
context:
space:
mode:
authorBen Kimock <kimockb@gmail.com>2023-03-10 22:30:02 -0500
committerBen Kimock <kimockb@gmail.com>2023-03-10 22:30:02 -0500
commit05f633bdaf11ca3485b7e9fd2af11e9a917af459 (patch)
tree13d070bafb527926160cbdb47a22237471acd71f /library
parent4e25fa242bf6a7ba8839dace0990163222b41460 (diff)
parent34be05e0970b1c50e1174c431d6a0303d72df4d2 (diff)
downloadrust-05f633bdaf11ca3485b7e9fd2af11e9a917af459.tar.gz
rust-05f633bdaf11ca3485b7e9fd2af11e9a917af459.zip
Merge from rustc
Diffstat (limited to 'library')
-rw-r--r--library/core/benches/lib.rs1
-rw-r--r--library/core/benches/tuple.rs22
-rw-r--r--library/core/src/intrinsics.rs19
-rw-r--r--library/core/src/intrinsics/mir.rs3
-rw-r--r--library/core/src/marker.rs2
-rw-r--r--library/core/src/slice/index.rs14
-rw-r--r--library/core/src/slice/mod.rs8
-rw-r--r--library/core/src/str/traits.rs72
-rw-r--r--library/core/src/tuple.rs48
-rw-r--r--library/std/src/f32.rs30
-rw-r--r--library/std/src/f32/tests.rs16
-rw-r--r--library/std/src/f64.rs30
-rw-r--r--library/std/src/f64/tests.rs16
-rw-r--r--library/std/src/lib.rs1
14 files changed, 228 insertions, 54 deletions
diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs
index e4100120d82..74ef0949b8a 100644
--- a/library/core/benches/lib.rs
+++ b/library/core/benches/lib.rs
@@ -20,6 +20,7 @@ mod ops;
 mod pattern;
 mod slice;
 mod str;
+mod tuple;
 
 /// Returns a `rand::Rng` seeded with a consistent seed.
 ///
diff --git a/library/core/benches/tuple.rs b/library/core/benches/tuple.rs
new file mode 100644
index 00000000000..d9ff9d0dd93
--- /dev/null
+++ b/library/core/benches/tuple.rs
@@ -0,0 +1,22 @@
+use rand::prelude::*;
+use test::{black_box, Bencher};
+
+#[bench]
+fn bench_tuple_comparison(b: &mut Bencher) {
+    let mut rng = black_box(super::bench_rng());
+
+    let data = black_box([
+        ("core::iter::adapters::Chain", 123_usize),
+        ("core::iter::adapters::Clone", 456_usize),
+        ("core::iter::adapters::Copie", 789_usize),
+        ("core::iter::adapters::Cycle", 123_usize),
+        ("core::iter::adapters::Flatt", 456_usize),
+        ("core::iter::adapters::TakeN", 789_usize),
+    ]);
+
+    b.iter(|| {
+        let x = data.choose(&mut rng).unwrap();
+        let y = data.choose(&mut rng).unwrap();
+        [x < y, x <= y, x > y, x >= y]
+    });
+}
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 18a90599c4d..c6321587adc 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1585,9 +1585,15 @@ extern "rust-intrinsic" {
 
     /// Returns the nearest integer to an `f32`. May raise an inexact floating-point exception
     /// if the argument is not an integer.
+    ///
+    /// The stabilized version of this intrinsic is
+    /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even)
     pub fn rintf32(x: f32) -> f32;
     /// Returns the nearest integer to an `f64`. May raise an inexact floating-point exception
     /// if the argument is not an integer.
+    ///
+    /// The stabilized version of this intrinsic is
+    /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even)
     pub fn rintf64(x: f64) -> f64;
 
     /// Returns the nearest integer to an `f32`.
@@ -1610,6 +1616,19 @@ extern "rust-intrinsic" {
     /// [`f64::round`](../../std/primitive.f64.html#method.round)
     pub fn roundf64(x: f64) -> f64;
 
+    /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number
+    /// with an even least significant digit.
+    ///
+    /// This intrinsic does not have a stable counterpart.
+    #[cfg(not(bootstrap))]
+    pub fn roundevenf32(x: f32) -> f32;
+    /// Returns the nearest integer to an `f64`. Rounds half-way cases to the number
+    /// with an even least significant digit.
+    ///
+    /// This intrinsic does not have a stable counterpart.
+    #[cfg(not(bootstrap))]
+    pub fn roundevenf64(x: f64) -> f64;
+
     /// Float addition that allows optimizations based on algebraic rules.
     /// May assume inputs are finite.
     ///
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
index 72db1d87ca3..d2d9771bdce 100644
--- a/library/core/src/intrinsics/mir.rs
+++ b/library/core/src/intrinsics/mir.rs
@@ -227,7 +227,7 @@
 //! are no resume and abort terminators, and terminators that might unwind do not have any way to
 //! indicate the unwind block.
 //!
-//!  - [`Goto`], [`Return`], [`Unreachable`], [`Drop`](Drop()), and [`DropAndReplace`] have associated functions.
+//!  - [`Goto`], [`Return`], [`Unreachable`] and [`Drop`](Drop()) have associated functions.
 //!  - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
 //!     - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
 //!       otherwise branch.
@@ -259,7 +259,6 @@ define!("mir_return", fn Return() -> BasicBlock);
 define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
 define!("mir_unreachable", fn Unreachable() -> BasicBlock);
 define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
-define!("mir_drop_and_replace", fn DropAndReplace<T>(place: T, value: T, goto: BasicBlock));
 define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T));
 define!("mir_storage_live", fn StorageLive<T>(local: T));
 define!("mir_storage_dead", fn StorageDead<T>(local: T));
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 520ae0edb09..427146941ad 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -324,7 +324,7 @@ pub trait StructuralEq {
 /// attempt to derive a `Copy` implementation, we'll get an error:
 ///
 /// ```text
-/// the trait `Copy` may not be implemented for this type; field `points` does not implement `Copy`
+/// the trait `Copy` cannot be implemented for this type; field `points` does not implement `Copy`
 /// ```
 ///
 /// Shared references (`&T`) are also `Copy`, so a type can be `Copy`, even when it holds
diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index c295a0e0645..3539353240a 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -2,6 +2,7 @@
 
 use crate::intrinsics::assert_unsafe_precondition;
 use crate::intrinsics::const_eval_select;
+use crate::intrinsics::unchecked_sub;
 use crate::ops;
 use crate::ptr;
 
@@ -371,25 +372,25 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
 
     #[inline]
     unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
-        let this = ops::Range { start: self.start, end: self.end };
+        let this = ops::Range { ..self };
         // SAFETY: the caller guarantees that `slice` is not dangling, so it
         // cannot be longer than `isize::MAX`. They also guarantee that
         // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
-        // so the call to `add` is safe.
-
+        // so the call to `add` is safe and the length calculation cannot overflow.
         unsafe {
             assert_unsafe_precondition!(
                 "slice::get_unchecked requires that the range is within the slice",
                 [T](this: ops::Range<usize>, slice: *const [T]) =>
                 this.end >= this.start && this.end <= slice.len()
             );
-            ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
+            let new_len = unchecked_sub(self.end, self.start);
+            ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len)
         }
     }
 
     #[inline]
     unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
-        let this = ops::Range { start: self.start, end: self.end };
+        let this = ops::Range { ..self };
         // SAFETY: see comments for `get_unchecked` above.
         unsafe {
             assert_unsafe_precondition!(
@@ -397,7 +398,8 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
                 [T](this: ops::Range<usize>, slice: *mut [T]) =>
                 this.end >= this.start && this.end <= slice.len()
             );
-            ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
+            let new_len = unchecked_sub(self.end, self.start);
+            ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len)
         }
     }
 
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 1cd86b445b0..d319b2bc37f 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -1695,7 +1695,13 @@ impl<T> [T] {
         let ptr = self.as_ptr();
 
         // SAFETY: Caller has to check that `0 <= mid <= self.len()`
-        unsafe { (from_raw_parts(ptr, mid), from_raw_parts(ptr.add(mid), len - mid)) }
+        unsafe {
+            assert_unsafe_precondition!(
+                "slice::split_at_unchecked requires the index to be within the slice",
+                (mid: usize, len: usize) => mid <= len
+            );
+            (from_raw_parts(ptr, mid), from_raw_parts(ptr.add(mid), len - mid))
+        }
     }
 
     /// Divides one mutable slice into two at an index, without doing bounds checking.
diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs
index 68f62ce8be5..41c097b55ee 100644
--- a/library/core/src/str/traits.rs
+++ b/library/core/src/str/traits.rs
@@ -1,6 +1,7 @@
 //! Trait implementations for `str`.
 
 use crate::cmp::Ordering;
+use crate::intrinsics::assert_unsafe_precondition;
 use crate::ops;
 use crate::ptr;
 use crate::slice::SliceIndex;
@@ -194,7 +195,21 @@ unsafe impl const SliceIndex<str> for ops::Range<usize> {
         let slice = slice as *const [u8];
         // SAFETY: the caller guarantees that `self` is in bounds of `slice`
         // which satisfies all the conditions for `add`.
-        let ptr = unsafe { slice.as_ptr().add(self.start) };
+        let ptr = unsafe {
+            let this = ops::Range { ..self };
+            assert_unsafe_precondition!(
+                "str::get_unchecked requires that the range is within the string slice",
+                (this: ops::Range<usize>, slice: *const [u8]) =>
+                // We'd like to check that the bounds are on char boundaries,
+                // but there's not really a way to do so without reading
+                // behind the pointer, which has aliasing implications.
+                // It's also not possible to move this check up to
+                // `str::get_unchecked` without adding a special function
+                // to `SliceIndex` just for this.
+                this.end >= this.start && this.end <= slice.len()
+            );
+            slice.as_ptr().add(self.start)
+        };
         let len = self.end - self.start;
         ptr::slice_from_raw_parts(ptr, len) as *const str
     }
@@ -202,7 +217,15 @@ unsafe impl const SliceIndex<str> for ops::Range<usize> {
     unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
         let slice = slice as *mut [u8];
         // SAFETY: see comments for `get_unchecked`.
-        let ptr = unsafe { slice.as_mut_ptr().add(self.start) };
+        let ptr = unsafe {
+            let this = ops::Range { ..self };
+            assert_unsafe_precondition!(
+                "str::get_unchecked_mut requires that the range is within the string slice",
+                (this: ops::Range<usize>, slice: *mut [u8]) =>
+                this.end >= this.start && this.end <= slice.len()
+            );
+            slice.as_mut_ptr().add(self.start)
+        };
         let len = self.end - self.start;
         ptr::slice_from_raw_parts_mut(ptr, len) as *mut str
     }
@@ -272,15 +295,13 @@ unsafe impl const SliceIndex<str> for ops::RangeTo<usize> {
     }
     #[inline]
     unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
-        let slice = slice as *const [u8];
-        let ptr = slice.as_ptr();
-        ptr::slice_from_raw_parts(ptr, self.end) as *const str
+        // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
+        unsafe { (0..self.end).get_unchecked(slice) }
     }
     #[inline]
     unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
-        let slice = slice as *mut [u8];
-        let ptr = slice.as_mut_ptr();
-        ptr::slice_from_raw_parts_mut(ptr, self.end) as *mut str
+        // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
+        unsafe { (0..self.end).get_unchecked_mut(slice) }
     }
     #[inline]
     fn index(self, slice: &str) -> &Self::Output {
@@ -343,20 +364,15 @@ unsafe impl const SliceIndex<str> for ops::RangeFrom<usize> {
     }
     #[inline]
     unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
-        let slice = slice as *const [u8];
-        // SAFETY: the caller guarantees that `self` is in bounds of `slice`
-        // which satisfies all the conditions for `add`.
-        let ptr = unsafe { slice.as_ptr().add(self.start) };
-        let len = slice.len() - self.start;
-        ptr::slice_from_raw_parts(ptr, len) as *const str
+        let len = (slice as *const [u8]).len();
+        // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
+        unsafe { (self.start..len).get_unchecked(slice) }
     }
     #[inline]
     unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
-        let slice = slice as *mut [u8];
-        // SAFETY: identical to `get_unchecked`.
-        let ptr = unsafe { slice.as_mut_ptr().add(self.start) };
-        let len = slice.len() - self.start;
-        ptr::slice_from_raw_parts_mut(ptr, len) as *mut str
+        let len = (slice as *mut [u8]).len();
+        // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
+        unsafe { (self.start..len).get_unchecked_mut(slice) }
     }
     #[inline]
     fn index(self, slice: &str) -> &Self::Output {
@@ -452,35 +468,29 @@ unsafe impl const SliceIndex<str> for ops::RangeToInclusive<usize> {
     type Output = str;
     #[inline]
     fn get(self, slice: &str) -> Option<&Self::Output> {
-        if self.end == usize::MAX { None } else { (..self.end + 1).get(slice) }
+        (0..=self.end).get(slice)
     }
     #[inline]
     fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
-        if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) }
+        (0..=self.end).get_mut(slice)
     }
     #[inline]
     unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
         // SAFETY: the caller must uphold the safety contract for `get_unchecked`.
-        unsafe { (..self.end + 1).get_unchecked(slice) }
+        unsafe { (0..=self.end).get_unchecked(slice) }
     }
     #[inline]
     unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
         // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
-        unsafe { (..self.end + 1).get_unchecked_mut(slice) }
+        unsafe { (0..=self.end).get_unchecked_mut(slice) }
     }
     #[inline]
     fn index(self, slice: &str) -> &Self::Output {
-        if self.end == usize::MAX {
-            str_index_overflow_fail();
-        }
-        (..self.end + 1).index(slice)
+        (0..=self.end).index(slice)
     }
     #[inline]
     fn index_mut(self, slice: &mut str) -> &mut Self::Output {
-        if self.end == usize::MAX {
-            str_index_overflow_fail();
-        }
-        (..self.end + 1).index_mut(slice)
+        (0..=self.end).index_mut(slice)
     }
 }
 
diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs
index 28275798f75..0620e7173bc 100644
--- a/library/core/src/tuple.rs
+++ b/library/core/src/tuple.rs
@@ -1,7 +1,7 @@
 // See src/libstd/primitive_docs.rs for documentation.
 
-use crate::cmp::Ordering::*;
-use crate::cmp::*;
+use crate::cmp::Ordering::{self, *};
+use crate::mem::transmute;
 
 // Recursive macro for implementing n-ary tuple functions and operations
 //
@@ -61,19 +61,19 @@ macro_rules! tuple_impls {
                 }
                 #[inline]
                 fn lt(&self, other: &($($T,)+)) -> bool {
-                    lexical_ord!(lt, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
+                    lexical_ord!(lt, Less, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
                 }
                 #[inline]
                 fn le(&self, other: &($($T,)+)) -> bool {
-                    lexical_ord!(le, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
+                    lexical_ord!(le, Less, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
                 }
                 #[inline]
                 fn ge(&self, other: &($($T,)+)) -> bool {
-                    lexical_ord!(ge, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
+                    lexical_ord!(ge, Greater, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
                 }
                 #[inline]
                 fn gt(&self, other: &($($T,)+)) -> bool {
-                    lexical_ord!(gt, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
+                    lexical_ord!(gt, Greater, $( ${ignore(T)} self.${index()}, other.${index()} ),+)
                 }
             }
         }
@@ -123,16 +123,38 @@ macro_rules! maybe_tuple_doc {
     };
 }
 
-// Constructs an expression that performs a lexical ordering using method $rel.
+#[inline]
+const fn ordering_is_some(c: Option<Ordering>, x: Ordering) -> bool {
+    // FIXME: Just use `==` once that's const-stable on `Option`s.
+    // This isn't using `match` because that optimizes worse due to
+    // making a two-step check (`Some` *then* the inner value).
+
+    // SAFETY: There's no public guarantee for `Option<Ordering>`,
+    // but we're core so we know that it's definitely a byte.
+    unsafe {
+        let c: i8 = transmute(c);
+        let x: i8 = transmute(Some(x));
+        c == x
+    }
+}
+
+// Constructs an expression that performs a lexical ordering using method `$rel`.
 // The values are interleaved, so the macro invocation for
-// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, a1, b1, a2, b2,
-// a3, b3)` (and similarly for `lexical_cmp`)
+// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1,
+// a2, b2, a3, b3)` (and similarly for `lexical_cmp`)
+//
+// `$ne_rel` is only used to determine the result after checking that they're
+// not equal, so `lt` and `le` can both just use `Less`.
 macro_rules! lexical_ord {
-    ($rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {
-        if $a != $b { lexical_ord!($rel, $a, $b) }
-        else { lexical_ord!($rel, $($rest_a, $rest_b),+) }
+    ($rel: ident, $ne_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{
+        let c = PartialOrd::partial_cmp(&$a, &$b);
+        if !ordering_is_some(c, Equal) { ordering_is_some(c, $ne_rel) }
+        else { lexical_ord!($rel, $ne_rel, $($rest_a, $rest_b),+) }
+    }};
+    ($rel: ident, $ne_rel: ident, $a:expr, $b:expr) => {
+        // Use the specific method for the last element
+        PartialOrd::$rel(&$a, &$b)
     };
-    ($rel: ident, $a:expr, $b:expr) => { ($a) . $rel (& $b) };
 }
 
 macro_rules! lexical_partial_cmp {
diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs
index 6b1f0cba82d..c7c33678fd3 100644
--- a/library/std/src/f32.rs
+++ b/library/std/src/f32.rs
@@ -78,10 +78,14 @@ impl f32 {
     /// let f = 3.3_f32;
     /// let g = -3.3_f32;
     /// let h = -3.7_f32;
+    /// let i = 3.5_f32;
+    /// let j = 4.5_f32;
     ///
     /// assert_eq!(f.round(), 3.0);
     /// assert_eq!(g.round(), -3.0);
     /// assert_eq!(h.round(), -4.0);
+    /// assert_eq!(i.round(), 4.0);
+    /// assert_eq!(j.round(), 5.0);
     /// ```
     #[rustc_allow_incoherent_impl]
     #[must_use = "method returns a new number and does not mutate the original value"]
@@ -91,6 +95,32 @@ impl f32 {
         unsafe { intrinsics::roundf32(self) }
     }
 
+    /// Returns the nearest integer to a number. Rounds half-way cases to the number
+    /// with an even least significant digit.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(round_ties_even)]
+    ///
+    /// let f = 3.3_f32;
+    /// let g = -3.3_f32;
+    /// let h = 3.5_f32;
+    /// let i = 4.5_f32;
+    ///
+    /// assert_eq!(f.round_ties_even(), 3.0);
+    /// assert_eq!(g.round_ties_even(), -3.0);
+    /// assert_eq!(h.round_ties_even(), 4.0);
+    /// assert_eq!(i.round_ties_even(), 4.0);
+    /// ```
+    #[rustc_allow_incoherent_impl]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "round_ties_even", issue = "96710")]
+    #[inline]
+    pub fn round_ties_even(self) -> f32 {
+        unsafe { intrinsics::rintf32(self) }
+    }
+
     /// Returns the integer part of `self`.
     /// This means that non-integer numbers are always truncated towards zero.
     ///
diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs
index 6ee295de616..e949def00bb 100644
--- a/library/std/src/f32/tests.rs
+++ b/library/std/src/f32/tests.rs
@@ -209,6 +209,7 @@ fn test_ceil() {
 
 #[test]
 fn test_round() {
+    assert_approx_eq!(2.5f32.round(), 3.0f32);
     assert_approx_eq!(1.0f32.round(), 1.0f32);
     assert_approx_eq!(1.3f32.round(), 1.0f32);
     assert_approx_eq!(1.5f32.round(), 2.0f32);
@@ -222,6 +223,21 @@ fn test_round() {
 }
 
 #[test]
+fn test_round_ties_even() {
+    assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32);
+    assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32);
+    assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32);
+    assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32);
+    assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32);
+    assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32);
+    assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32);
+    assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32);
+    assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32);
+    assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32);
+    assert_approx_eq!((-1.7f32).round_ties_even(), -2.0f32);
+}
+
+#[test]
 fn test_trunc() {
     assert_approx_eq!(1.0f32.trunc(), 1.0f32);
     assert_approx_eq!(1.3f32.trunc(), 1.0f32);
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index 16359766b51..b1faa670307 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -78,10 +78,14 @@ impl f64 {
     /// let f = 3.3_f64;
     /// let g = -3.3_f64;
     /// let h = -3.7_f64;
+    /// let i = 3.5_f64;
+    /// let j = 4.5_f64;
     ///
     /// assert_eq!(f.round(), 3.0);
     /// assert_eq!(g.round(), -3.0);
     /// assert_eq!(h.round(), -4.0);
+    /// assert_eq!(i.round(), 4.0);
+    /// assert_eq!(j.round(), 5.0);
     /// ```
     #[rustc_allow_incoherent_impl]
     #[must_use = "method returns a new number and does not mutate the original value"]
@@ -91,6 +95,32 @@ impl f64 {
         unsafe { intrinsics::roundf64(self) }
     }
 
+    /// Returns the nearest integer to a number. Rounds half-way cases to the number
+    /// with an even least significant digit.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(round_ties_even)]
+    ///
+    /// let f = 3.3_f64;
+    /// let g = -3.3_f64;
+    /// let h = 3.5_f64;
+    /// let i = 4.5_f64;
+    ///
+    /// assert_eq!(f.round_ties_even(), 3.0);
+    /// assert_eq!(g.round_ties_even(), -3.0);
+    /// assert_eq!(h.round_ties_even(), 4.0);
+    /// assert_eq!(i.round_ties_even(), 4.0);
+    /// ```
+    #[rustc_allow_incoherent_impl]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "round_ties_even", issue = "96710")]
+    #[inline]
+    pub fn round_ties_even(self) -> f64 {
+        unsafe { intrinsics::rintf64(self) }
+    }
+
     /// Returns the integer part of `self`.
     /// This means that non-integer numbers are always truncated towards zero.
     ///
diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs
index 5b039d445ce..53d351cceef 100644
--- a/library/std/src/f64/tests.rs
+++ b/library/std/src/f64/tests.rs
@@ -199,6 +199,7 @@ fn test_ceil() {
 
 #[test]
 fn test_round() {
+    assert_approx_eq!(2.5f64.round(), 3.0f64);
     assert_approx_eq!(1.0f64.round(), 1.0f64);
     assert_approx_eq!(1.3f64.round(), 1.0f64);
     assert_approx_eq!(1.5f64.round(), 2.0f64);
@@ -212,6 +213,21 @@ fn test_round() {
 }
 
 #[test]
+fn test_round_ties_even() {
+    assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64);
+    assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64);
+    assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64);
+    assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64);
+    assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64);
+    assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64);
+    assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64);
+    assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64);
+    assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64);
+    assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64);
+    assert_approx_eq!((-1.7f64).round_ties_even(), -2.0f64);
+}
+
+#[test]
 fn test_trunc() {
     assert_approx_eq!(1.0f64.trunc(), 1.0f64);
     assert_approx_eq!(1.3f64.trunc(), 1.0f64);
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index b62f3ad29d3..7837dd276d2 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -304,6 +304,7 @@
 #![feature(provide_any)]
 #![feature(ptr_as_uninit)]
 #![feature(raw_os_nonzero)]
+#![feature(round_ties_even)]
 #![feature(slice_internals)]
 #![feature(slice_ptr_get)]
 #![feature(std_internals)]