about summary refs log tree commit diff
path: root/library/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/core/src')
-rw-r--r--library/core/src/bstr.rs4
-rw-r--r--library/core/src/convert/mod.rs1
-rw-r--r--library/core/src/fmt/mod.rs66
-rw-r--r--library/core/src/intrinsics/mod.rs2
-rw-r--r--library/core/src/iter/sources/successors.rs16
-rw-r--r--library/core/src/lib.rs3
-rw-r--r--library/core/src/marker.rs4
-rw-r--r--library/core/src/num/dec2flt/common.rs27
-rw-r--r--library/core/src/num/dec2flt/decimal.rs421
-rw-r--r--library/core/src/num/dec2flt/decimal_seq.rs379
-rw-r--r--library/core/src/num/dec2flt/float.rs272
-rw-r--r--library/core/src/num/dec2flt/lemire.rs22
-rw-r--r--library/core/src/num/dec2flt/mod.rs37
-rw-r--r--library/core/src/num/dec2flt/number.rs88
-rw-r--r--library/core/src/num/dec2flt/parse.rs10
-rw-r--r--library/core/src/num/dec2flt/slow.rs28
-rw-r--r--library/core/src/num/f32.rs6
-rw-r--r--library/core/src/num/f64.rs6
-rw-r--r--library/core/src/pat.rs62
-rw-r--r--library/core/src/slice/mod.rs37
20 files changed, 832 insertions, 659 deletions
diff --git a/library/core/src/bstr.rs b/library/core/src/bstr.rs
index 74e07f3d242..ae84fd8adb6 100644
--- a/library/core/src/bstr.rs
+++ b/library/core/src/bstr.rs
@@ -151,7 +151,9 @@ impl fmt::Display for ByteStr {
         };
         let nchars: usize = self
             .utf8_chunks()
-            .map(|chunk| chunk.valid().len() + if chunk.invalid().is_empty() { 0 } else { 1 })
+            .map(|chunk| {
+                chunk.valid().chars().count() + if chunk.invalid().is_empty() { 0 } else { 1 }
+            })
             .sum();
         let padding = f.width().unwrap_or(0).saturating_sub(nchars);
         let fill = f.fill();
diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs
index e468f4f0f7e..43fd54f881d 100644
--- a/library/core/src/convert/mod.rs
+++ b/library/core/src/convert/mod.rs
@@ -778,7 +778,6 @@ impl<T> From<T> for T {
 ///
 /// [#64715]: https://github.com/rust-lang/rust/issues/64715
 #[stable(feature = "convert_infallible", since = "1.34.0")]
-#[allow(unused_attributes)] // FIXME(#58633): do a principled fix instead.
 #[rustc_reservation_impl = "permitting this impl would forbid us from adding \
                             `impl<T> From<!> for T` later; see rust-lang/rust#64715 for details"]
 impl<T> From<!> for T {
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 764e7fff33e..3f60bb067d6 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -1693,49 +1693,41 @@ impl<'a> Formatter<'a> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn pad(&mut self, s: &str) -> Result {
-        // Make sure there's a fast path up front
+        // Make sure there's a fast path up front.
         if self.options.width.is_none() && self.options.precision.is_none() {
             return self.buf.write_str(s);
         }
-        // The `precision` field can be interpreted as a `max-width` for the
+
+        // The `precision` field can be interpreted as a maximum width for the
         // string being formatted.
-        let s = if let Some(max) = self.options.precision {
-            // If our string is longer that the precision, then we must have
-            // truncation. However other flags like `fill`, `width` and `align`
-            // must act as always.
-            if let Some((i, _)) = s.char_indices().nth(max) {
-                // LLVM here can't prove that `..i` won't panic `&s[..i]`, but
-                // we know that it can't panic. Use `get` + `unwrap_or` to avoid
-                // `unsafe` and otherwise don't emit any panic-related code
-                // here.
-                s.get(..i).unwrap_or(s)
-            } else {
-                &s
-            }
+        let (s, char_count) = if let Some(max_char_count) = self.options.precision {
+            let mut iter = s.char_indices();
+            let remaining = match iter.advance_by(max_char_count) {
+                Ok(()) => 0,
+                Err(remaining) => remaining.get(),
+            };
+            // SAFETY: The offset of `.char_indices()` is guaranteed to be
+            // in-bounds and between character boundaries.
+            let truncated = unsafe { s.get_unchecked(..iter.offset()) };
+            (truncated, max_char_count - remaining)
         } else {
-            &s
+            // Use the optimized char counting algorithm for the full string.
+            (s, s.chars().count())
         };
-        // The `width` field is more of a `min-width` parameter at this point.
-        match self.options.width {
-            // If we're under the maximum length, and there's no minimum length
-            // requirements, then we can just emit the string
-            None => self.buf.write_str(s),
-            Some(width) => {
-                let chars_count = s.chars().count();
-                // If we're under the maximum width, check if we're over the minimum
-                // width, if so it's as easy as just emitting the string.
-                if chars_count >= width {
-                    self.buf.write_str(s)
-                }
-                // If we're under both the maximum and the minimum width, then fill
-                // up the minimum width with the specified string + some alignment.
-                else {
-                    let align = Alignment::Left;
-                    let post_padding = self.padding(width - chars_count, align)?;
-                    self.buf.write_str(s)?;
-                    post_padding.write(self)
-                }
-            }
+
+        // The `width` field is more of a minimum width parameter at this point.
+        if let Some(width) = self.options.width
+            && char_count < width
+        {
+            // If we're under the minimum width, then fill up the minimum width
+            // with the specified string + some alignment.
+            let post_padding = self.padding(width - char_count, Alignment::Left)?;
+            self.buf.write_str(s)?;
+            post_padding.write(self)
+        } else {
+            // If we're over the minimum width or there is no minimum width, we
+            // can just emit the string.
+            self.buf.write_str(s)
         }
     }
 
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index b405e1d413a..6af647b137d 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -10,7 +10,7 @@
 //!
 //! In order to make an intrinsic usable at compile-time, it needs to be declared in the "new"
 //! style, i.e. as a `#[rustc_intrinsic]` function, not inside an `extern` block. Then copy the
-//! implementation from <https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics> to
+//! implementation from <https://github.com/rust-lang/miri/blob/master/src/intrinsics> to
 //! <https://github.com/rust-lang/rust/blob/master/compiler/rustc_const_eval/src/interpret/intrinsics.rs>
 //! and make the intrinsic declaration a `const fn`.
 //!
diff --git a/library/core/src/iter/sources/successors.rs b/library/core/src/iter/sources/successors.rs
index e14c9235e55..5466131903f 100644
--- a/library/core/src/iter/sources/successors.rs
+++ b/library/core/src/iter/sources/successors.rs
@@ -1,11 +1,16 @@
 use crate::fmt;
 use crate::iter::FusedIterator;
 
-/// Creates a new iterator where each successive item is computed based on the preceding one.
+/// Creates an iterator which, starting from an initial item,
+/// computes each successive item from the preceding one.
 ///
-/// The iterator starts with the given first item (if any)
-/// and calls the given `FnMut(&T) -> Option<T>` closure to compute each item’s successor.
-/// The iterator will yield the `T`s returned from the closure.
+/// This iterator stores an optional item (`Option<T>`) and a successor closure (`impl FnMut(&T) -> Option<T>`).
+/// Its `next` method returns the stored optional item and
+/// if it is `Some(val)` calls the stored closure on `&val` to compute and store its successor.
+/// The iterator will apply the closure successively to the stored option's value until the option is `None`.
+/// This also means that once the stored option is `None` it will remain `None`,
+/// as the closure will not be called again, so the created iterator is a [`FusedIterator`].
+/// The iterator's items will be the initial item and all of its successors as calculated by the successor closure.
 ///
 /// ```
 /// use std::iter::successors;
@@ -24,7 +29,8 @@ where
     Successors { next: first, succ }
 }
 
-/// A new iterator where each successive item is computed based on the preceding one.
+/// An iterator which, starting from an initial item,
+/// computes each successive item from the preceding one.
 ///
 /// This `struct` is created by the [`iter::successors()`] function.
 /// See its documentation for more.
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index db68f472c42..987fa93d598 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -202,14 +202,17 @@
 //
 // Target features:
 // tidy-alphabetical-start
+#![feature(aarch64_unstable_target_feature)]
 #![feature(arm_target_feature)]
 #![feature(avx512_target_feature)]
 #![feature(hexagon_target_feature)]
+#![feature(keylocker_x86)]
 #![feature(loongarch_target_feature)]
 #![feature(mips_target_feature)]
 #![feature(powerpc_target_feature)]
 #![feature(riscv_target_feature)]
 #![feature(rtm_target_feature)]
+#![feature(s390x_target_feature)]
 #![feature(sha512_sm_x86)]
 #![feature(sse4a_target_feature)]
 #![feature(tbm_target_feature)]
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index b6fd823f77e..e234f105b0b 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -453,8 +453,8 @@ impl Copy for ! {}
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized> Copy for &T {}
 
-/// Marker trait for the types that are allowed in union fields, unsafe fields,
-/// and unsafe binder types.
+/// Marker trait for the types that are allowed in union fields and unsafe
+/// binder types.
 ///
 /// Implemented for:
 /// * `&T`, `&mut T` for all `T`,
diff --git a/library/core/src/num/dec2flt/common.rs b/library/core/src/num/dec2flt/common.rs
index 4dadf406ae8..a140a311c45 100644
--- a/library/core/src/num/dec2flt/common.rs
+++ b/library/core/src/num/dec2flt/common.rs
@@ -8,12 +8,12 @@ pub(crate) trait ByteSlice {
     /// Writes a 64-bit integer as 8 bytes in little-endian order.
     fn write_u64(&mut self, value: u64);
 
-    /// Calculate the offset of a slice from another.
+    /// Calculate the difference in length between two slices.
     fn offset_from(&self, other: &Self) -> isize;
 
     /// Iteratively parse and consume digits from bytes.
-    /// Returns the same bytes with consumed digits being
-    /// elided.
+    ///
+    /// Returns the same bytes with consumed digits being elided. Breaks on invalid digits.
     fn parse_digits(&self, func: impl FnMut(u8)) -> &Self;
 }
 
@@ -39,11 +39,11 @@ impl ByteSlice for [u8] {
     fn parse_digits(&self, mut func: impl FnMut(u8)) -> &Self {
         let mut s = self;
 
-        while let Some((c, s_next)) = s.split_first() {
+        while let Some((c, rest)) = s.split_first() {
             let c = c.wrapping_sub(b'0');
             if c < 10 {
                 func(c);
-                s = s_next;
+                s = rest;
             } else {
                 break;
             }
@@ -53,7 +53,9 @@ impl ByteSlice for [u8] {
     }
 }
 
-/// Determine if 8 bytes are all decimal digits.
+/// Determine if all characters in an 8-byte byte string (represented as a `u64`) are all decimal
+/// digits.
+///
 /// This does not care about the order in which the bytes were loaded.
 pub(crate) fn is_8digits(v: u64) -> bool {
     let a = v.wrapping_add(0x4646_4646_4646_4646);
@@ -61,19 +63,20 @@ pub(crate) fn is_8digits(v: u64) -> bool {
     (a | b) & 0x8080_8080_8080_8080 == 0
 }
 
-/// A custom 64-bit floating point type, representing `f * 2^e`.
-/// e is biased, so it be directly shifted into the exponent bits.
+/// A custom 64-bit floating point type, representing `m * 2^p`.
+/// p is biased, so it be directly shifted into the exponent bits.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
 pub struct BiasedFp {
     /// The significant digits.
-    pub f: u64,
+    pub m: u64,
     /// The biased, binary exponent.
-    pub e: i32,
+    pub p_biased: i32,
 }
 
 impl BiasedFp {
+    /// Represent `0 ^ p`
     #[inline]
-    pub const fn zero_pow2(e: i32) -> Self {
-        Self { f: 0, e }
+    pub const fn zero_pow2(p_biased: i32) -> Self {
+        Self { m: 0, p_biased }
     }
 }
diff --git a/library/core/src/num/dec2flt/decimal.rs b/library/core/src/num/dec2flt/decimal.rs
index b37724ba62d..db7176c1243 100644
--- a/library/core/src/num/dec2flt/decimal.rs
+++ b/library/core/src/num/dec2flt/decimal.rs
@@ -1,358 +1,87 @@
-//! Arbitrary-precision decimal class for fallback algorithms.
-//!
-//! This is only used if the fast-path (native floats) and
-//! the Eisel-Lemire algorithm are unable to unambiguously
-//! determine the float.
-//!
-//! The technique used is "Simple Decimal Conversion", developed
-//! by Nigel Tao and Ken Thompson. A detailed description of the
-//! algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion",
-//! available online: <https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html>.
-
-use crate::num::dec2flt::common::{ByteSlice, is_8digits};
-
-#[derive(Clone)]
-pub(super) struct Decimal {
-    /// The number of significant digits in the decimal.
-    pub num_digits: usize,
-    /// The offset of the decimal point in the significant digits.
-    pub decimal_point: i32,
-    /// If the number of significant digits stored in the decimal is truncated.
-    pub truncated: bool,
-    /// Buffer of the raw digits, in the range [0, 9].
-    pub digits: [u8; Self::MAX_DIGITS],
+//! Representation of a float as the significant digits and exponent.
+
+use crate::num::dec2flt::float::RawFloat;
+use crate::num::dec2flt::fpu::set_precision;
+
+const INT_POW10: [u64; 16] = [
+    1,
+    10,
+    100,
+    1000,
+    10000,
+    100000,
+    1000000,
+    10000000,
+    100000000,
+    1000000000,
+    10000000000,
+    100000000000,
+    1000000000000,
+    10000000000000,
+    100000000000000,
+    1000000000000000,
+];
+
+/// A floating point number with up to 64 bits of mantissa and an `i64` exponent.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct Decimal {
+    pub exponent: i64,
+    pub mantissa: u64,
+    pub negative: bool,
+    pub many_digits: bool,
 }
 
-impl Default for Decimal {
-    fn default() -> Self {
-        Self { num_digits: 0, decimal_point: 0, truncated: false, digits: [0; Self::MAX_DIGITS] }
+impl Decimal {
+    /// Detect if the float can be accurately reconstructed from native floats.
+    #[inline]
+    fn can_use_fast_path<F: RawFloat>(&self) -> bool {
+        F::MIN_EXPONENT_FAST_PATH <= self.exponent
+            && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH
+            && self.mantissa <= F::MAX_MANTISSA_FAST_PATH
+            && !self.many_digits
     }
-}
 
-impl Decimal {
-    /// The maximum number of digits required to unambiguously round a float.
-    ///
-    /// For a double-precision IEEE 754 float, this required 767 digits,
-    /// so we store the max digits + 1.
-    ///
-    /// We can exactly represent a float in radix `b` from radix 2 if
-    /// `b` is divisible by 2. This function calculates the exact number of
-    /// digits required to exactly represent that float.
-    ///
-    /// According to the "Handbook of Floating Point Arithmetic",
-    /// for IEEE754, with emin being the min exponent, p2 being the
-    /// precision, and b being the radix, the number of digits follows as:
+    /// Try turning the decimal into an exact float representation, using machine-sized integers
+    /// and floats.
     ///
-    /// `−emin + p2 + ⌊(emin + 1) log(2, b) − log(1 − 2^(−p2), b)⌋`
+    /// This is extracted into a separate function so that it can be attempted before constructing
+    /// a Decimal. This only works if both the mantissa and the exponent
+    /// can be exactly represented as a machine float, since IEE-754 guarantees
+    /// no rounding will occur.
     ///
-    /// For f32, this follows as:
-    ///     emin = -126
-    ///     p2 = 24
-    ///
-    /// For f64, this follows as:
-    ///     emin = -1022
-    ///     p2 = 53
-    ///
-    /// In Python:
-    ///     `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))`
-    pub(super) const MAX_DIGITS: usize = 768;
-    /// The max digits that can be exactly represented in a 64-bit integer.
-    pub(super) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19;
-    pub(super) const DECIMAL_POINT_RANGE: i32 = 2047;
-
-    /// Append a digit to the buffer.
-    pub(super) fn try_add_digit(&mut self, digit: u8) {
-        if self.num_digits < Self::MAX_DIGITS {
-            self.digits[self.num_digits] = digit;
-        }
-        self.num_digits += 1;
-    }
-
-    /// Trim trailing zeros from the buffer.
-    pub(super) fn trim(&mut self) {
-        // All of the following calls to `Decimal::trim` can't panic because:
-        //
-        //  1. `parse_decimal` sets `num_digits` to a max of `Decimal::MAX_DIGITS`.
-        //  2. `right_shift` sets `num_digits` to `write_index`, which is bounded by `num_digits`.
-        //  3. `left_shift` `num_digits` to a max of `Decimal::MAX_DIGITS`.
-        //
-        // Trim is only called in `right_shift` and `left_shift`.
-        debug_assert!(self.num_digits <= Self::MAX_DIGITS);
-        while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 {
-            self.num_digits -= 1;
-        }
-    }
-
-    pub(super) fn round(&self) -> u64 {
-        if self.num_digits == 0 || self.decimal_point < 0 {
-            return 0;
-        } else if self.decimal_point > 18 {
-            return 0xFFFF_FFFF_FFFF_FFFF_u64;
-        }
-        let dp = self.decimal_point as usize;
-        let mut n = 0_u64;
-        for i in 0..dp {
-            n *= 10;
-            if i < self.num_digits {
-                n += self.digits[i] as u64;
-            }
-        }
-        let mut round_up = false;
-        if dp < self.num_digits {
-            round_up = self.digits[dp] >= 5;
-            if self.digits[dp] == 5 && dp + 1 == self.num_digits {
-                round_up = self.truncated || ((dp != 0) && (1 & self.digits[dp - 1] != 0))
-            }
-        }
-        if round_up {
-            n += 1;
-        }
-        n
-    }
-
-    /// Computes decimal * 2^shift.
-    pub(super) fn left_shift(&mut self, shift: usize) {
-        if self.num_digits == 0 {
-            return;
-        }
-        let num_new_digits = number_of_digits_decimal_left_shift(self, shift);
-        let mut read_index = self.num_digits;
-        let mut write_index = self.num_digits + num_new_digits;
-        let mut n = 0_u64;
-        while read_index != 0 {
-            read_index -= 1;
-            write_index -= 1;
-            n += (self.digits[read_index] as u64) << shift;
-            let quotient = n / 10;
-            let remainder = n - (10 * quotient);
-            if write_index < Self::MAX_DIGITS {
-                self.digits[write_index] = remainder as u8;
-            } else if remainder > 0 {
-                self.truncated = true;
-            }
-            n = quotient;
-        }
-        while n > 0 {
-            write_index -= 1;
-            let quotient = n / 10;
-            let remainder = n - (10 * quotient);
-            if write_index < Self::MAX_DIGITS {
-                self.digits[write_index] = remainder as u8;
-            } else if remainder > 0 {
-                self.truncated = true;
-            }
-            n = quotient;
-        }
-        self.num_digits += num_new_digits;
-        if self.num_digits > Self::MAX_DIGITS {
-            self.num_digits = Self::MAX_DIGITS;
-        }
-        self.decimal_point += num_new_digits as i32;
-        self.trim();
-    }
-
-    /// Computes decimal * 2^-shift.
-    pub(super) fn right_shift(&mut self, shift: usize) {
-        let mut read_index = 0;
-        let mut write_index = 0;
-        let mut n = 0_u64;
-        while (n >> shift) == 0 {
-            if read_index < self.num_digits {
-                n = (10 * n) + self.digits[read_index] as u64;
-                read_index += 1;
-            } else if n == 0 {
-                return;
+    /// There is an exception: disguised fast-path cases, where we can shift
+    /// powers-of-10 from the exponent to the significant digits.
+    pub fn try_fast_path<F: RawFloat>(&self) -> Option<F> {
+        // Here we need to work around <https://github.com/rust-lang/rust/issues/114479>.
+        // The fast path crucially depends on arithmetic being rounded to the correct number of bits
+        // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision
+        // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit.
+        // The `set_precision` function takes care of setting the precision on architectures which
+        // require setting it by changing the global state (like the control word of the x87 FPU).
+        let _cw = set_precision::<F>();
+
+        if !self.can_use_fast_path::<F>() {
+            return None;
+        }
+
+        let value = if self.exponent <= F::MAX_EXPONENT_FAST_PATH {
+            // normal fast path
+            let value = F::from_u64(self.mantissa);
+            if self.exponent < 0 {
+                value / F::pow10_fast_path((-self.exponent) as _)
             } else {
-                while (n >> shift) == 0 {
-                    n *= 10;
-                    read_index += 1;
-                }
-                break;
-            }
-        }
-        self.decimal_point -= read_index as i32 - 1;
-        if self.decimal_point < -Self::DECIMAL_POINT_RANGE {
-            // `self = Self::Default()`, but without the overhead of clearing `digits`.
-            self.num_digits = 0;
-            self.decimal_point = 0;
-            self.truncated = false;
-            return;
-        }
-        let mask = (1_u64 << shift) - 1;
-        while read_index < self.num_digits {
-            let new_digit = (n >> shift) as u8;
-            n = (10 * (n & mask)) + self.digits[read_index] as u64;
-            read_index += 1;
-            self.digits[write_index] = new_digit;
-            write_index += 1;
-        }
-        while n > 0 {
-            let new_digit = (n >> shift) as u8;
-            n = 10 * (n & mask);
-            if write_index < Self::MAX_DIGITS {
-                self.digits[write_index] = new_digit;
-                write_index += 1;
-            } else if new_digit > 0 {
-                self.truncated = true;
-            }
-        }
-        self.num_digits = write_index;
-        self.trim();
-    }
-}
-
-/// Parse a big integer representation of the float as a decimal.
-pub(super) fn parse_decimal(mut s: &[u8]) -> Decimal {
-    let mut d = Decimal::default();
-    let start = s;
-
-    while let Some((&b'0', s_next)) = s.split_first() {
-        s = s_next;
-    }
-
-    s = s.parse_digits(|digit| d.try_add_digit(digit));
-
-    if let Some((b'.', s_next)) = s.split_first() {
-        s = s_next;
-        let first = s;
-        // Skip leading zeros.
-        if d.num_digits == 0 {
-            while let Some((&b'0', s_next)) = s.split_first() {
-                s = s_next;
-            }
-        }
-        while s.len() >= 8 && d.num_digits + 8 < Decimal::MAX_DIGITS {
-            let v = s.read_u64();
-            if !is_8digits(v) {
-                break;
+                value * F::pow10_fast_path(self.exponent as _)
             }
-            d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030);
-            d.num_digits += 8;
-            s = &s[8..];
-        }
-        s = s.parse_digits(|digit| d.try_add_digit(digit));
-        d.decimal_point = s.len() as i32 - first.len() as i32;
-    }
-    if d.num_digits != 0 {
-        // Ignore the trailing zeros if there are any
-        let mut n_trailing_zeros = 0;
-        for &c in start[..(start.len() - s.len())].iter().rev() {
-            if c == b'0' {
-                n_trailing_zeros += 1;
-            } else if c != b'.' {
-                break;
-            }
-        }
-        d.decimal_point += n_trailing_zeros as i32;
-        d.num_digits -= n_trailing_zeros;
-        d.decimal_point += d.num_digits as i32;
-        if d.num_digits > Decimal::MAX_DIGITS {
-            d.truncated = true;
-            d.num_digits = Decimal::MAX_DIGITS;
-        }
-    }
-    if let Some((&ch, s_next)) = s.split_first() {
-        if ch == b'e' || ch == b'E' {
-            s = s_next;
-            let mut neg_exp = false;
-            if let Some((&ch, s_next)) = s.split_first() {
-                neg_exp = ch == b'-';
-                if ch == b'-' || ch == b'+' {
-                    s = s_next;
-                }
+        } else {
+            // disguised fast path
+            let shift = self.exponent - F::MAX_EXPONENT_FAST_PATH;
+            let mantissa = self.mantissa.checked_mul(INT_POW10[shift as usize])?;
+            if mantissa > F::MAX_MANTISSA_FAST_PATH {
+                return None;
             }
-            let mut exp_num = 0_i32;
-
-            s.parse_digits(|digit| {
-                if exp_num < 0x10000 {
-                    exp_num = 10 * exp_num + digit as i32;
-                }
-            });
+            F::from_u64(mantissa) * F::pow10_fast_path(F::MAX_EXPONENT_FAST_PATH as _)
+        };
 
-            d.decimal_point += if neg_exp { -exp_num } else { exp_num };
-        }
-    }
-    for i in d.num_digits..Decimal::MAX_DIGITS_WITHOUT_OVERFLOW {
-        d.digits[i] = 0;
-    }
-    d
-}
-
-fn number_of_digits_decimal_left_shift(d: &Decimal, mut shift: usize) -> usize {
-    #[rustfmt::skip]
-    const TABLE: [u16; 65] = [
-        0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, 0x181D, 0x2024,
-        0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, 0x3073, 0x3080, 0x388E, 0x389C,
-        0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169,
-        0x5180, 0x5998, 0x59B0, 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B,
-        0x72AA, 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, 0x8C02,
-        0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, 0x051C, 0x051C,
-    ];
-    #[rustfmt::skip]
-    const TABLE_POW5: [u8; 0x051C] = [
-        5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9, 0, 6, 2, 5, 1,
-        9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5,
-        1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2,
-        5, 1, 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6,
-        9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1,
-        6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9,
-        1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6,
-        4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1,
-        4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2,
-        3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6,
-        2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5,
-        4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2,
-        5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5,
-        3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4,
-        6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6,
-        1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0,
-        6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, 1, 2, 5, 3,
-        6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8,
-        9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4,
-        7, 0, 1, 7, 7, 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
-        0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3, 7, 3, 6, 7, 5,
-        4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7,
-        7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8,
-        8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0,
-        9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0,
-        8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1,
-        0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2,
-        5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8,
-        9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0,
-        6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3,
-        8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2,
-        6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4,
-        9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1,
-        1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8,
-        2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5,
-        8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1,
-        3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, 3, 8, 7, 7, 7, 8,
-        7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0,
-        6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2,
-        5, 5, 6, 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
-        8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2,
-        3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3,
-        8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2,
-        2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5,
-    ];
-
-    shift &= 63;
-    let x_a = TABLE[shift];
-    let x_b = TABLE[shift + 1];
-    let num_new_digits = (x_a >> 11) as _;
-    let pow5_a = (0x7FF & x_a) as usize;
-    let pow5_b = (0x7FF & x_b) as usize;
-    let pow5 = &TABLE_POW5[pow5_a..];
-    for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) {
-        if i >= d.num_digits {
-            return num_new_digits - 1;
-        } else if d.digits[i] == p5 {
-            continue;
-        } else if d.digits[i] < p5 {
-            return num_new_digits - 1;
-        } else {
-            return num_new_digits;
-        }
+        if self.negative { Some(-value) } else { Some(value) }
     }
-    num_new_digits
 }
diff --git a/library/core/src/num/dec2flt/decimal_seq.rs b/library/core/src/num/dec2flt/decimal_seq.rs
new file mode 100644
index 00000000000..de22280c001
--- /dev/null
+++ b/library/core/src/num/dec2flt/decimal_seq.rs
@@ -0,0 +1,379 @@
+//! Arbitrary-precision decimal type used by fallback algorithms.
+//!
+//! This is only used if the fast-path (native floats) and
+//! the Eisel-Lemire algorithm are unable to unambiguously
+//! determine the float.
+//!
+//! The technique used is "Simple Decimal Conversion", developed
+//! by Nigel Tao and Ken Thompson. A detailed description of the
+//! algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion",
+//! available online: <https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html>.
+
+use crate::num::dec2flt::common::{ByteSlice, is_8digits};
+
+/// A decimal floating-point number, represented as a sequence of decimal digits.
+#[derive(Clone, Debug, PartialEq)]
+pub struct DecimalSeq {
+    /// The number of significant digits in the decimal.
+    pub num_digits: usize,
+    /// The offset of the decimal point in the significant digits.
+    pub decimal_point: i32,
+    /// If the number of significant digits stored in the decimal is truncated.
+    pub truncated: bool,
+    /// Buffer of the raw digits, in the range [0, 9].
+    pub digits: [u8; Self::MAX_DIGITS],
+}
+
+impl Default for DecimalSeq {
+    fn default() -> Self {
+        Self { num_digits: 0, decimal_point: 0, truncated: false, digits: [0; Self::MAX_DIGITS] }
+    }
+}
+
+impl DecimalSeq {
+    /// The maximum number of digits required to unambiguously round up to a 64-bit float.
+    ///
+    /// For an IEEE 754 binary64 float, this required 767 digits. So we store the max digits + 1.
+    ///
+    /// We can exactly represent a float in radix `b` from radix 2 if
+    /// `b` is divisible by 2. This function calculates the exact number of
+    /// digits required to exactly represent that float.
+    ///
+    /// According to the "Handbook of Floating Point Arithmetic",
+    /// for IEEE754, with `emin` being the min exponent, `p2` being the
+    /// precision, and `b` being the radix, the number of digits follows as:
+    ///
+    /// `−emin + p2 + ⌊(emin + 1) log(2, b) − log(1 − 2^(−p2), b)⌋`
+    ///
+    /// For f32, this follows as:
+    ///     emin = -126
+    ///     p2 = 24
+    ///
+    /// For f64, this follows as:
+    ///     emin = -1022
+    ///     p2 = 53
+    ///
+    /// In Python:
+    ///     `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))`
+    pub const MAX_DIGITS: usize = 768;
+
+    /// The max decimal digits that can be exactly represented in a 64-bit integer.
+    pub(super) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19;
+    pub(super) const DECIMAL_POINT_RANGE: i32 = 2047;
+
+    /// Append a digit to the buffer if it fits.
+    // FIXME(tgross35): it may be better for this to return an option
+    // FIXME(tgross35): incrementing the digit counter even if we don't push anything
+    // seems incorrect.
+    pub(super) fn try_add_digit(&mut self, digit: u8) {
+        if self.num_digits < Self::MAX_DIGITS {
+            self.digits[self.num_digits] = digit;
+        }
+        self.num_digits += 1;
+    }
+
+    /// Trim trailing zeros from the buffer.
+    // FIXME(tgross35): this could be `.rev().position()` if perf is okay
+    pub fn trim(&mut self) {
+        // All of the following calls to `DecimalSeq::trim` can't panic because:
+        //
+        //  1. `parse_decimal` sets `num_digits` to a max of `DecimalSeq::MAX_DIGITS`.
+        //  2. `right_shift` sets `num_digits` to `write_index`, which is bounded by `num_digits`.
+        //  3. `left_shift` `num_digits` to a max of `DecimalSeq::MAX_DIGITS`.
+        //
+        // Trim is only called in `right_shift` and `left_shift`.
+        debug_assert!(self.num_digits <= Self::MAX_DIGITS);
+        while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 {
+            self.num_digits -= 1;
+        }
+    }
+
+    pub(super) fn round(&self) -> u64 {
+        if self.num_digits == 0 || self.decimal_point < 0 {
+            return 0;
+        } else if self.decimal_point >= Self::MAX_DIGITS_WITHOUT_OVERFLOW as i32 {
+            return 0xFFFF_FFFF_FFFF_FFFF_u64;
+        }
+
+        let dp = self.decimal_point as usize;
+        let mut n = 0_u64;
+
+        for i in 0..dp {
+            n *= 10;
+            if i < self.num_digits {
+                n += self.digits[i] as u64;
+            }
+        }
+
+        let mut round_up = false;
+
+        if dp < self.num_digits {
+            round_up = self.digits[dp] >= 5;
+            if self.digits[dp] == 5 && dp + 1 == self.num_digits {
+                round_up = self.truncated || ((dp != 0) && (1 & self.digits[dp - 1] != 0))
+            }
+        }
+
+        if round_up {
+            n += 1;
+        }
+        n
+    }
+
+    /// Computes decimal * 2^shift.
+    pub(super) fn left_shift(&mut self, shift: usize) {
+        if self.num_digits == 0 {
+            return;
+        }
+        let num_new_digits = number_of_digits_decimal_left_shift(self, shift);
+        let mut read_index = self.num_digits;
+        let mut write_index = self.num_digits + num_new_digits;
+        let mut n = 0_u64;
+
+        while read_index != 0 {
+            read_index -= 1;
+            write_index -= 1;
+            n += (self.digits[read_index] as u64) << shift;
+            let quotient = n / 10;
+            let remainder = n - (10 * quotient);
+            if write_index < Self::MAX_DIGITS {
+                self.digits[write_index] = remainder as u8;
+            } else if remainder > 0 {
+                self.truncated = true;
+            }
+            n = quotient;
+        }
+
+        while n > 0 {
+            write_index -= 1;
+            let quotient = n / 10;
+            let remainder = n - (10 * quotient);
+            if write_index < Self::MAX_DIGITS {
+                self.digits[write_index] = remainder as u8;
+            } else if remainder > 0 {
+                self.truncated = true;
+            }
+            n = quotient;
+        }
+
+        self.num_digits += num_new_digits;
+
+        if self.num_digits > Self::MAX_DIGITS {
+            self.num_digits = Self::MAX_DIGITS;
+        }
+
+        self.decimal_point += num_new_digits as i32;
+        self.trim();
+    }
+
+    /// Computes decimal * 2^-shift.
+    pub(super) fn right_shift(&mut self, shift: usize) {
+        let mut read_index = 0;
+        let mut write_index = 0;
+        let mut n = 0_u64;
+        while (n >> shift) == 0 {
+            if read_index < self.num_digits {
+                n = (10 * n) + self.digits[read_index] as u64;
+                read_index += 1;
+            } else if n == 0 {
+                return;
+            } else {
+                while (n >> shift) == 0 {
+                    n *= 10;
+                    read_index += 1;
+                }
+                break;
+            }
+        }
+        self.decimal_point -= read_index as i32 - 1;
+        if self.decimal_point < -Self::DECIMAL_POINT_RANGE {
+            // `self = Self::Default()`, but without the overhead of clearing `digits`.
+            self.num_digits = 0;
+            self.decimal_point = 0;
+            self.truncated = false;
+            return;
+        }
+        let mask = (1_u64 << shift) - 1;
+        while read_index < self.num_digits {
+            let new_digit = (n >> shift) as u8;
+            n = (10 * (n & mask)) + self.digits[read_index] as u64;
+            read_index += 1;
+            self.digits[write_index] = new_digit;
+            write_index += 1;
+        }
+        while n > 0 {
+            let new_digit = (n >> shift) as u8;
+            n = 10 * (n & mask);
+            if write_index < Self::MAX_DIGITS {
+                self.digits[write_index] = new_digit;
+                write_index += 1;
+            } else if new_digit > 0 {
+                self.truncated = true;
+            }
+        }
+        self.num_digits = write_index;
+        self.trim();
+    }
+}
+
+/// Parse a big integer representation of the float as a decimal.
+pub fn parse_decimal_seq(mut s: &[u8]) -> DecimalSeq {
+    let mut d = DecimalSeq::default();
+    let start = s;
+
+    while let Some((&b'0', s_next)) = s.split_first() {
+        s = s_next;
+    }
+
+    s = s.parse_digits(|digit| d.try_add_digit(digit));
+
+    if let Some((b'.', s_next)) = s.split_first() {
+        s = s_next;
+        let first = s;
+        // Skip leading zeros.
+        if d.num_digits == 0 {
+            while let Some((&b'0', s_next)) = s.split_first() {
+                s = s_next;
+            }
+        }
+        while s.len() >= 8 && d.num_digits + 8 < DecimalSeq::MAX_DIGITS {
+            let v = s.read_u64();
+            if !is_8digits(v) {
+                break;
+            }
+            d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030);
+            d.num_digits += 8;
+            s = &s[8..];
+        }
+        s = s.parse_digits(|digit| d.try_add_digit(digit));
+        d.decimal_point = s.len() as i32 - first.len() as i32;
+    }
+
+    if d.num_digits != 0 {
+        // Ignore the trailing zeros if there are any
+        let mut n_trailing_zeros = 0;
+        for &c in start[..(start.len() - s.len())].iter().rev() {
+            if c == b'0' {
+                n_trailing_zeros += 1;
+            } else if c != b'.' {
+                break;
+            }
+        }
+        d.decimal_point += n_trailing_zeros as i32;
+        d.num_digits -= n_trailing_zeros;
+        d.decimal_point += d.num_digits as i32;
+        if d.num_digits > DecimalSeq::MAX_DIGITS {
+            d.truncated = true;
+            d.num_digits = DecimalSeq::MAX_DIGITS;
+        }
+    }
+
+    if let Some((&ch, s_next)) = s.split_first() {
+        if ch == b'e' || ch == b'E' {
+            s = s_next;
+            let mut neg_exp = false;
+            if let Some((&ch, s_next)) = s.split_first() {
+                neg_exp = ch == b'-';
+                if ch == b'-' || ch == b'+' {
+                    s = s_next;
+                }
+            }
+            let mut exp_num = 0_i32;
+
+            s.parse_digits(|digit| {
+                if exp_num < 0x10000 {
+                    exp_num = 10 * exp_num + digit as i32;
+                }
+            });
+
+            d.decimal_point += if neg_exp { -exp_num } else { exp_num };
+        }
+    }
+
+    for i in d.num_digits..DecimalSeq::MAX_DIGITS_WITHOUT_OVERFLOW {
+        d.digits[i] = 0;
+    }
+
+    d
+}
+
+fn number_of_digits_decimal_left_shift(d: &DecimalSeq, mut shift: usize) -> usize {
+    #[rustfmt::skip]
+    const TABLE: [u16; 65] = [
+        0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, 0x181D, 0x2024,
+        0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, 0x3073, 0x3080, 0x388E, 0x389C,
+        0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169,
+        0x5180, 0x5998, 0x59B0, 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B,
+        0x72AA, 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, 0x8C02,
+        0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, 0x051C, 0x051C,
+    ];
+    #[rustfmt::skip]
+    const TABLE_POW5: [u8; 0x051C] = [
+        5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9, 0, 6, 2, 5, 1,
+        9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5,
+        1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2,
+        5, 1, 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6,
+        9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1,
+        6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9,
+        1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6,
+        4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1,
+        4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2,
+        3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6,
+        2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5,
+        4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2,
+        5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5,
+        3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4,
+        6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6,
+        1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0,
+        6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, 1, 2, 5, 3,
+        6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8,
+        9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4,
+        7, 0, 1, 7, 7, 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
+        0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3, 7, 3, 6, 7, 5,
+        4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7,
+        7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8,
+        8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0,
+        9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0,
+        8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1,
+        0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2,
+        5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8,
+        9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0,
+        6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3,
+        8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2,
+        6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4,
+        9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1,
+        1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8,
+        2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5,
+        8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1,
+        3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, 3, 8, 7, 7, 7, 8,
+        7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0,
+        6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2,
+        5, 5, 6, 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
+        8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2,
+        3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3,
+        8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2,
+        2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5,
+    ];
+
+    shift &= 63;
+    let x_a = TABLE[shift];
+    let x_b = TABLE[shift + 1];
+    let num_new_digits = (x_a >> 11) as _;
+    let pow5_a = (0x7FF & x_a) as usize;
+    let pow5_b = (0x7FF & x_b) as usize;
+    let pow5 = &TABLE_POW5[pow5_a..];
+
+    for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) {
+        if i >= d.num_digits {
+            return num_new_digits - 1;
+        } else if d.digits[i] == p5 {
+            continue;
+        } else if d.digits[i] < p5 {
+            return num_new_digits - 1;
+        } else {
+            return num_new_digits;
+        }
+    }
+
+    num_new_digits
+}
diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs
index da57aa9a546..b8a28a67569 100644
--- a/library/core/src/num/dec2flt/float.rs
+++ b/library/core/src/num/dec2flt/float.rs
@@ -1,14 +1,57 @@
 //! Helper trait for generic float types.
 
+use core::f64;
+
 use crate::fmt::{Debug, LowerExp};
 use crate::num::FpCategory;
-use crate::ops::{Add, Div, Mul, Neg};
+use crate::ops::{self, Add, Div, Mul, Neg};
+
+/// Lossy `as` casting between two types.
+pub trait CastInto<T: Copy>: Copy {
+    fn cast(self) -> T;
+}
+
+/// Collection of traits that allow us to be generic over integer size.
+pub trait Integer:
+    Sized
+    + Clone
+    + Copy
+    + Debug
+    + ops::Shr<u32, Output = Self>
+    + ops::Shl<u32, Output = Self>
+    + ops::BitAnd<Output = Self>
+    + ops::BitOr<Output = Self>
+    + PartialEq
+    + CastInto<i16>
+{
+    const ZERO: Self;
+    const ONE: Self;
+}
+
+macro_rules! int {
+    ($($ty:ty),+) => {
+        $(
+            impl CastInto<i16> for $ty {
+                fn cast(self) -> i16 {
+                    self as i16
+                }
+            }
+
+            impl Integer for $ty {
+                const ZERO: Self = 0;
+                const ONE: Self = 1;
+            }
+        )+
+    }
+}
+
+int!(u32, u64);
 
-/// A helper trait to avoid duplicating basically all the conversion code for `f32` and `f64`.
+/// A helper trait to avoid duplicating basically all the conversion code for IEEE floats.
 ///
 /// See the parent module's doc comment for why this is necessary.
 ///
-/// Should **never ever** be implemented for other types or be used outside the dec2flt module.
+/// Should **never ever** be implemented for other types or be used outside the `dec2flt` module.
 #[doc(hidden)]
 pub trait RawFloat:
     Sized
@@ -24,62 +67,107 @@ pub trait RawFloat:
     + Copy
     + Debug
 {
+    /// The unsigned integer with the same size as the float
+    type Int: Integer + Into<u64>;
+
+    /* general constants */
+
     const INFINITY: Self;
     const NEG_INFINITY: Self;
     const NAN: Self;
     const NEG_NAN: Self;
 
-    /// The number of bits in the significand, *excluding* the hidden bit.
-    const MANTISSA_EXPLICIT_BITS: usize;
-
-    // Round-to-even only happens for negative values of q
-    // when q ≥ −4 in the 64-bit case and when q ≥ −17 in
-    // the 32-bitcase.
-    //
-    // When q ≥ 0,we have that 5^q ≤ 2m+1. In the 64-bit case,we
-    // have 5^q ≤ 2m+1 ≤ 2^54 or q ≤ 23. In the 32-bit case,we have
-    // 5^q ≤ 2m+1 ≤ 2^25 or q ≤ 10.
-    //
-    // When q < 0, we have w ≥ (2m+1)×5^−q. We must have that w < 2^64
-    // so (2m+1)×5^−q < 2^64. We have that 2m+1 > 2^53 (64-bit case)
-    // or 2m+1 > 2^24 (32-bit case). Hence,we must have 2^53×5^−q < 2^64
-    // (64-bit) and 2^24×5^−q < 2^64 (32-bit). Hence we have 5^−q < 2^11
-    // or q ≥ −4 (64-bit case) and 5^−q < 2^40 or q ≥ −17 (32-bitcase).
-    //
-    // Thus we have that we only need to round ties to even when
-    // we have that q ∈ [−4,23](in the 64-bit case) or q∈[−17,10]
-    // (in the 32-bit case). In both cases,the power of five(5^|q|)
-    // fits in a 64-bit word.
-    const MIN_EXPONENT_ROUND_TO_EVEN: i32;
-    const MAX_EXPONENT_ROUND_TO_EVEN: i32;
+    /// Bit width of the float
+    const BITS: u32;
 
-    // Minimum exponent that for a fast path case, or `-⌊(MANTISSA_EXPLICIT_BITS+1)/log2(5)⌋`
-    const MIN_EXPONENT_FAST_PATH: i64;
+    /// The number of bits in the significand, *including* the hidden bit.
+    const SIG_TOTAL_BITS: u32;
 
-    // Maximum exponent that for a fast path case, or `⌊(MANTISSA_EXPLICIT_BITS+1)/log2(5)⌋`
-    const MAX_EXPONENT_FAST_PATH: i64;
+    const EXP_MASK: Self::Int;
+    const SIG_MASK: Self::Int;
 
-    // Maximum exponent that can be represented for a disguised-fast path case.
-    // This is `MAX_EXPONENT_FAST_PATH + ⌊(MANTISSA_EXPLICIT_BITS+1)/log2(10)⌋`
-    const MAX_EXPONENT_DISGUISED_FAST_PATH: i64;
-
-    // Minimum exponent value `-(1 << (EXP_BITS - 1)) + 1`.
-    const MINIMUM_EXPONENT: i32;
+    /// The number of bits in the significand, *excluding* the hidden bit.
+    const SIG_BITS: u32 = Self::SIG_TOTAL_BITS - 1;
+
+    /// Number of bits in the exponent.
+    const EXP_BITS: u32 = Self::BITS - Self::SIG_BITS - 1;
+
+    /// The saturated (maximum bitpattern) value of the exponent, i.e. the infinite
+    /// representation.
+    ///
+    /// This shifted fully right, use `EXP_MASK` for the shifted value.
+    const EXP_SAT: u32 = (1 << Self::EXP_BITS) - 1;
+
+    /// Signed version of `EXP_SAT` since we convert a lot.
+    const INFINITE_POWER: i32 = Self::EXP_SAT as i32;
+
+    /// The exponent bias value. This is also the maximum value of the exponent.
+    const EXP_BIAS: u32 = Self::EXP_SAT >> 1;
+
+    /// Minimum exponent value of normal values.
+    const EXP_MIN: i32 = -(Self::EXP_BIAS as i32 - 1);
+
+    /// Round-to-even only happens for negative values of q
+    /// when q ≥ −4 in the 64-bit case and when q ≥ −17 in
+    /// the 32-bitcase.
+    ///
+    /// When q ≥ 0,we have that 5^q ≤ 2m+1. In the 64-bit case,we
+    /// have 5^q ≤ 2m+1 ≤ 2^54 or q ≤ 23. In the 32-bit case,we have
+    /// 5^q ≤ 2m+1 ≤ 2^25 or q ≤ 10.
+    ///
+    /// When q < 0, we have w ≥ (2m+1)×5^−q. We must have that w < 2^64
+    /// so (2m+1)×5^−q < 2^64. We have that 2m+1 > 2^53 (64-bit case)
+    /// or 2m+1 > 2^24 (32-bit case). Hence,we must have 2^53×5^−q < 2^64
+    /// (64-bit) and 2^24×5^−q < 2^64 (32-bit). Hence we have 5^−q < 2^11
+    /// or q ≥ −4 (64-bit case) and 5^−q < 2^40 or q ≥ −17 (32-bitcase).
+    ///
+    /// Thus we have that we only need to round ties to even when
+    /// we have that q ∈ [−4,23](in the 64-bit case) or q∈[−17,10]
+    /// (in the 32-bit case). In both cases,the power of five(5^|q|)
+    /// fits in a 64-bit word.
+    const MIN_EXPONENT_ROUND_TO_EVEN: i32;
+    const MAX_EXPONENT_ROUND_TO_EVEN: i32;
 
-    // Largest exponent value `(1 << EXP_BITS) - 1`.
-    const INFINITE_POWER: i32;
+    /* limits related to Fast pathing */
+
+    /// Largest decimal exponent for a non-infinite value.
+    ///
+    /// This is the max exponent in binary converted to the max exponent in decimal. Allows fast
+    /// pathing anything larger than `10^LARGEST_POWER_OF_TEN`, which will round to infinity.
+    const LARGEST_POWER_OF_TEN: i32 = {
+        let largest_pow2 = Self::EXP_BIAS + 1;
+        pow2_to_pow10(largest_pow2 as i64) as i32
+    };
+
+    /// Smallest decimal exponent for a non-zero value. This allows for fast pathing anything
+    /// smaller than `10^SMALLEST_POWER_OF_TEN`, which will round to zero.
+    ///
+    /// The smallest power of ten is represented by `⌊log10(2^-n / (2^64 - 1))⌋`, where `n` is
+    /// the smallest power of two. The `2^64 - 1)` denomenator comes from the number of values
+    /// that are representable by the intermediate storage format. I don't actually know _why_
+    /// the storage format is relevant here.
+    ///
+    /// The values may be calculated using the formula. Unfortunately we cannot calculate them at
+    /// compile time since intermediates exceed the range of an `f64`.
+    const SMALLEST_POWER_OF_TEN: i32;
 
-    // Index (in bits) of the sign.
-    const SIGN_INDEX: usize;
+    /// Maximum exponent for a fast path case, or `⌊(SIG_BITS+1)/log2(5)⌋`
+    // assuming FLT_EVAL_METHOD = 0
+    const MAX_EXPONENT_FAST_PATH: i64 = {
+        let log2_5 = f64::consts::LOG2_10 - 1.0;
+        (Self::SIG_TOTAL_BITS as f64 / log2_5) as i64
+    };
 
-    // Smallest decimal exponent for a non-zero value.
-    const SMALLEST_POWER_OF_TEN: i32;
+    /// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋`
+    const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH;
 
-    // Largest decimal exponent for a non-infinite value.
-    const LARGEST_POWER_OF_TEN: i32;
+    /// Maximum exponent that can be represented for a disguised-fast path case.
+    /// This is `MAX_EXPONENT_FAST_PATH + ⌊(SIG_BITS+1)/log2(10)⌋`
+    const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 =
+        Self::MAX_EXPONENT_FAST_PATH + (Self::SIG_TOTAL_BITS as f64 / f64::consts::LOG2_10) as i64;
 
-    // Maximum mantissa for the fast-path (`1 << 53` for f64).
-    const MAX_MANTISSA_FAST_PATH: u64 = 2_u64 << Self::MANTISSA_EXPLICIT_BITS;
+    /// Maximum mantissa for the fast-path (`1 << 53` for f64).
+    const MAX_MANTISSA_FAST_PATH: u64 = 1 << Self::SIG_TOTAL_BITS;
 
     /// Converts integer into float through an as cast.
     /// This is only called in the fast-path algorithm, and therefore
@@ -96,27 +184,51 @@ pub trait RawFloat:
     /// Returns the category that this number falls into.
     fn classify(self) -> FpCategory;
 
+    /// Transmute to the integer representation
+    fn to_bits(self) -> Self::Int;
+
     /// Returns the mantissa, exponent and sign as integers.
-    fn integer_decode(self) -> (u64, i16, i8);
+    ///
+    /// That is, this returns `(m, p, s)` such that `s * m * 2^p` represents the original float.
+    /// For 0, the exponent will be `-(EXP_BIAS + SIG_BITS`, which is the
+    /// minimum subnormal power.
+    fn integer_decode(self) -> (u64, i16, i8) {
+        let bits = self.to_bits();
+        let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 };
+        let mut exponent: i16 = ((bits & Self::EXP_MASK) >> Self::SIG_BITS).cast();
+        let mantissa = if exponent == 0 {
+            (bits & Self::SIG_MASK) << 1
+        } else {
+            (bits & Self::SIG_MASK) | (Self::Int::ONE << Self::SIG_BITS)
+        };
+        // Exponent bias + mantissa shift
+        exponent -= (Self::EXP_BIAS + Self::SIG_BITS) as i16;
+        (mantissa.into(), exponent, sign)
+    }
+}
+
+/// Solve for `b` in `10^b = 2^a`
+const fn pow2_to_pow10(a: i64) -> i64 {
+    let res = (a as f64) / f64::consts::LOG2_10;
+    res as i64
 }
 
 impl RawFloat for f32 {
+    type Int = u32;
+
     const INFINITY: Self = f32::INFINITY;
     const NEG_INFINITY: Self = f32::NEG_INFINITY;
     const NAN: Self = f32::NAN;
     const NEG_NAN: Self = -f32::NAN;
 
-    const MANTISSA_EXPLICIT_BITS: usize = 23;
+    const BITS: u32 = 32;
+    const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS;
+    const EXP_MASK: Self::Int = Self::EXP_MASK;
+    const SIG_MASK: Self::Int = Self::MAN_MASK;
+
     const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17;
     const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10;
-    const MIN_EXPONENT_FAST_PATH: i64 = -10; // assuming FLT_EVAL_METHOD = 0
-    const MAX_EXPONENT_FAST_PATH: i64 = 10;
-    const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = 17;
-    const MINIMUM_EXPONENT: i32 = -127;
-    const INFINITE_POWER: i32 = 0xFF;
-    const SIGN_INDEX: usize = 31;
     const SMALLEST_POWER_OF_TEN: i32 = -65;
-    const LARGEST_POWER_OF_TEN: i32 = 38;
 
     #[inline]
     fn from_u64(v: u64) -> Self {
@@ -136,16 +248,8 @@ impl RawFloat for f32 {
         TABLE[exponent & 15]
     }
 
-    /// Returns the mantissa, exponent and sign as integers.
-    fn integer_decode(self) -> (u64, i16, i8) {
-        let bits = self.to_bits();
-        let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 };
-        let mut exponent: i16 = ((bits >> 23) & 0xff) as i16;
-        let mantissa =
-            if exponent == 0 { (bits & 0x7fffff) << 1 } else { (bits & 0x7fffff) | 0x800000 };
-        // Exponent bias + mantissa shift
-        exponent -= 127 + 23;
-        (mantissa as u64, exponent, sign)
+    fn to_bits(self) -> Self::Int {
+        self.to_bits()
     }
 
     fn classify(self) -> FpCategory {
@@ -154,22 +258,21 @@ impl RawFloat for f32 {
 }
 
 impl RawFloat for f64 {
-    const INFINITY: Self = f64::INFINITY;
-    const NEG_INFINITY: Self = f64::NEG_INFINITY;
-    const NAN: Self = f64::NAN;
-    const NEG_NAN: Self = -f64::NAN;
+    type Int = u64;
+
+    const INFINITY: Self = Self::INFINITY;
+    const NEG_INFINITY: Self = Self::NEG_INFINITY;
+    const NAN: Self = Self::NAN;
+    const NEG_NAN: Self = -Self::NAN;
+
+    const BITS: u32 = 64;
+    const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS;
+    const EXP_MASK: Self::Int = Self::EXP_MASK;
+    const SIG_MASK: Self::Int = Self::MAN_MASK;
 
-    const MANTISSA_EXPLICIT_BITS: usize = 52;
     const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -4;
     const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23;
-    const MIN_EXPONENT_FAST_PATH: i64 = -22; // assuming FLT_EVAL_METHOD = 0
-    const MAX_EXPONENT_FAST_PATH: i64 = 22;
-    const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = 37;
-    const MINIMUM_EXPONENT: i32 = -1023;
-    const INFINITE_POWER: i32 = 0x7FF;
-    const SIGN_INDEX: usize = 63;
     const SMALLEST_POWER_OF_TEN: i32 = -342;
-    const LARGEST_POWER_OF_TEN: i32 = 308;
 
     #[inline]
     fn from_u64(v: u64) -> Self {
@@ -190,19 +293,8 @@ impl RawFloat for f64 {
         TABLE[exponent & 31]
     }
 
-    /// Returns the mantissa, exponent and sign as integers.
-    fn integer_decode(self) -> (u64, i16, i8) {
-        let bits = self.to_bits();
-        let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
-        let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
-        let mantissa = if exponent == 0 {
-            (bits & 0xfffffffffffff) << 1
-        } else {
-            (bits & 0xfffffffffffff) | 0x10000000000000
-        };
-        // Exponent bias + mantissa shift
-        exponent -= 1023 + 52;
-        (mantissa, exponent, sign)
+    fn to_bits(self) -> Self::Int {
+        self.to_bits()
     }
 
     fn classify(self) -> FpCategory {
diff --git a/library/core/src/num/dec2flt/lemire.rs b/library/core/src/num/dec2flt/lemire.rs
index 01642e1b111..f84929a03c1 100644
--- a/library/core/src/num/dec2flt/lemire.rs
+++ b/library/core/src/num/dec2flt/lemire.rs
@@ -38,7 +38,7 @@ pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
     // Normalize our significant digits, so the most-significant bit is set.
     let lz = w.leading_zeros();
     w <<= lz;
-    let (lo, hi) = compute_product_approx(q, w, F::MANTISSA_EXPLICIT_BITS + 3);
+    let (lo, hi) = compute_product_approx(q, w, F::SIG_BITS as usize + 3);
     if lo == 0xFFFF_FFFF_FFFF_FFFF {
         // If we have failed to approximate w x 5^-q with our 128-bit value.
         // Since the addition of 1 could lead to an overflow which could then
@@ -61,8 +61,8 @@ pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
         }
     }
     let upperbit = (hi >> 63) as i32;
-    let mut mantissa = hi >> (upperbit + 64 - F::MANTISSA_EXPLICIT_BITS as i32 - 3);
-    let mut power2 = power(q as i32) + upperbit - lz as i32 - F::MINIMUM_EXPONENT;
+    let mut mantissa = hi >> (upperbit + 64 - F::SIG_BITS as i32 - 3);
+    let mut power2 = power(q as i32) + upperbit - lz as i32 - F::EXP_MIN + 1;
     if power2 <= 0 {
         if -power2 + 1 >= 64 {
             // Have more than 64 bits below the minimum exponent, must be 0.
@@ -72,8 +72,8 @@ pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
         mantissa >>= -power2 + 1;
         mantissa += mantissa & 1;
         mantissa >>= 1;
-        power2 = (mantissa >= (1_u64 << F::MANTISSA_EXPLICIT_BITS)) as i32;
-        return BiasedFp { f: mantissa, e: power2 };
+        power2 = (mantissa >= (1_u64 << F::SIG_BITS)) as i32;
+        return BiasedFp { m: mantissa, p_biased: power2 };
     }
     // Need to handle rounding ties. Normally, we need to round up,
     // but if we fall right in between and we have an even basis, we
@@ -89,8 +89,8 @@ pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
     if lo <= 1
         && q >= F::MIN_EXPONENT_ROUND_TO_EVEN as i64
         && q <= F::MAX_EXPONENT_ROUND_TO_EVEN as i64
-        && mantissa & 3 == 1
-        && (mantissa << (upperbit + 64 - F::MANTISSA_EXPLICIT_BITS as i32 - 3)) == hi
+        && mantissa & 0b11 == 0b01
+        && (mantissa << (upperbit + 64 - F::SIG_BITS as i32 - 3)) == hi
     {
         // Zero the lowest bit, so we don't round up.
         mantissa &= !1_u64;
@@ -98,20 +98,20 @@ pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
     // Round-to-even, then shift the significant digits into place.
     mantissa += mantissa & 1;
     mantissa >>= 1;
-    if mantissa >= (2_u64 << F::MANTISSA_EXPLICIT_BITS) {
+    if mantissa >= (2_u64 << F::SIG_BITS) {
         // Rounding up overflowed, so the carry bit is set. Set the
         // mantissa to 1 (only the implicit, hidden bit is set) and
         // increase the exponent.
-        mantissa = 1_u64 << F::MANTISSA_EXPLICIT_BITS;
+        mantissa = 1_u64 << F::SIG_BITS;
         power2 += 1;
     }
     // Zero out the hidden bit.
-    mantissa &= !(1_u64 << F::MANTISSA_EXPLICIT_BITS);
+    mantissa &= !(1_u64 << F::SIG_BITS);
     if power2 >= F::INFINITE_POWER {
         // Exponent is above largest normal value, must be infinite.
         return fp_inf;
     }
-    BiasedFp { f: mantissa, e: power2 }
+    BiasedFp { m: mantissa, p_biased: power2 }
 }
 
 /// Calculate a base 2 exponent from a decimal exponent.
diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs
index 6dca7406845..d1a0e1db313 100644
--- a/library/core/src/num/dec2flt/mod.rs
+++ b/library/core/src/num/dec2flt/mod.rs
@@ -3,8 +3,8 @@
 //! # Problem statement
 //!
 //! We are given a decimal string such as `12.34e56`. This string consists of integral (`12`),
-//! fractional (`34`), and exponent (`56`) parts. All parts are optional and interpreted as zero
-//! when missing.
+//! fractional (`34`), and exponent (`56`) parts. All parts are optional and interpreted as a
+//! default value (1 or 0) when missing.
 //!
 //! We seek the IEEE 754 floating point number that is closest to the exact value of the decimal
 //! string. It is well-known that many decimal strings do not have terminating representations in
@@ -67,6 +67,18 @@
 //! "such that the exponent +/- the number of decimal digits fits into a 64 bit integer".
 //! Larger exponents are accepted, but we don't do arithmetic with them, they are immediately
 //! turned into {positive,negative} {zero,infinity}.
+//!
+//! # Notation
+//!
+//! This module uses the same notation as the Lemire paper:
+//!
+//! - `m`: binary mantissa; always nonnegative
+//! - `p`: binary exponent; a signed integer
+//! - `w`: decimal significand; always nonnegative
+//! - `q`: decimal exponent; a signed integer
+//!
+//! This gives `m * 2^p` for the binary floating-point number, with `w * 10^q` as the decimal
+//! equivalent.
 
 #![doc(hidden)]
 #![unstable(
@@ -85,14 +97,14 @@ use crate::fmt;
 use crate::str::FromStr;
 
 mod common;
-mod decimal;
+pub mod decimal;
+pub mod decimal_seq;
 mod fpu;
 mod slow;
 mod table;
 // float is used in flt2dec, and all are used in unit tests.
 pub mod float;
 pub mod lemire;
-pub mod number;
 pub mod parse;
 
 macro_rules! from_str_float_impl {
@@ -220,10 +232,10 @@ pub fn pfe_invalid() -> ParseFloatError {
 }
 
 /// Converts a `BiasedFp` to the closest machine float type.
-fn biased_fp_to_float<T: RawFloat>(x: BiasedFp) -> T {
-    let mut word = x.f;
-    word |= (x.e as u64) << T::MANTISSA_EXPLICIT_BITS;
-    T::from_u64_bits(word)
+fn biased_fp_to_float<F: RawFloat>(x: BiasedFp) -> F {
+    let mut word = x.m;
+    word |= (x.p_biased as u64) << F::SIG_BITS;
+    F::from_u64_bits(word)
 }
 
 /// Converts a decimal string into a floating point number.
@@ -260,12 +272,15 @@ pub fn dec2flt<F: RawFloat>(s: &str) -> Result<F, ParseFloatError> {
     // redundantly using the Eisel-Lemire algorithm if it was unable to
     // correctly round on the first pass.
     let mut fp = compute_float::<F>(num.exponent, num.mantissa);
-    if num.many_digits && fp.e >= 0 && fp != compute_float::<F>(num.exponent, num.mantissa + 1) {
-        fp.e = -1;
+    if num.many_digits
+        && fp.p_biased >= 0
+        && fp != compute_float::<F>(num.exponent, num.mantissa + 1)
+    {
+        fp.p_biased = -1;
     }
     // Unable to correctly round the float using the Eisel-Lemire algorithm.
     // Fallback to a slower, but always correct algorithm.
-    if fp.e < 0 {
+    if fp.p_biased < 0 {
         fp = parse_long_mantissa::<F>(s);
     }
 
diff --git a/library/core/src/num/dec2flt/number.rs b/library/core/src/num/dec2flt/number.rs
deleted file mode 100644
index 2538991564a..00000000000
--- a/library/core/src/num/dec2flt/number.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-//! Representation of a float as the significant digits and exponent.
-
-use crate::num::dec2flt::float::RawFloat;
-use crate::num::dec2flt::fpu::set_precision;
-
-#[rustfmt::skip]
-const INT_POW10: [u64; 16] = [
-    1,
-    10,
-    100,
-    1000,
-    10000,
-    100000,
-    1000000,
-    10000000,
-    100000000,
-    1000000000,
-    10000000000,
-    100000000000,
-    1000000000000,
-    10000000000000,
-    100000000000000,
-    1000000000000000,
-];
-
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
-pub struct Number {
-    pub exponent: i64,
-    pub mantissa: u64,
-    pub negative: bool,
-    pub many_digits: bool,
-}
-
-impl Number {
-    /// Detect if the float can be accurately reconstructed from native floats.
-    #[inline]
-    fn is_fast_path<F: RawFloat>(&self) -> bool {
-        F::MIN_EXPONENT_FAST_PATH <= self.exponent
-            && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH
-            && self.mantissa <= F::MAX_MANTISSA_FAST_PATH
-            && !self.many_digits
-    }
-
-    /// The fast path algorithm using machine-sized integers and floats.
-    ///
-    /// This is extracted into a separate function so that it can be attempted before constructing
-    /// a Decimal. This only works if both the mantissa and the exponent
-    /// can be exactly represented as a machine float, since IEE-754 guarantees
-    /// no rounding will occur.
-    ///
-    /// There is an exception: disguised fast-path cases, where we can shift
-    /// powers-of-10 from the exponent to the significant digits.
-    pub fn try_fast_path<F: RawFloat>(&self) -> Option<F> {
-        // Here we need to work around <https://github.com/rust-lang/rust/issues/114479>.
-        // The fast path crucially depends on arithmetic being rounded to the correct number of bits
-        // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision
-        // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit.
-        // The `set_precision` function takes care of setting the precision on architectures which
-        // require setting it by changing the global state (like the control word of the x87 FPU).
-        let _cw = set_precision::<F>();
-
-        if self.is_fast_path::<F>() {
-            let mut value = if self.exponent <= F::MAX_EXPONENT_FAST_PATH {
-                // normal fast path
-                let value = F::from_u64(self.mantissa);
-                if self.exponent < 0 {
-                    value / F::pow10_fast_path((-self.exponent) as _)
-                } else {
-                    value * F::pow10_fast_path(self.exponent as _)
-                }
-            } else {
-                // disguised fast path
-                let shift = self.exponent - F::MAX_EXPONENT_FAST_PATH;
-                let mantissa = self.mantissa.checked_mul(INT_POW10[shift as usize])?;
-                if mantissa > F::MAX_MANTISSA_FAST_PATH {
-                    return None;
-                }
-                F::from_u64(mantissa) * F::pow10_fast_path(F::MAX_EXPONENT_FAST_PATH as _)
-            };
-            if self.negative {
-                value = -value;
-            }
-            Some(value)
-        } else {
-            None
-        }
-    }
-}
diff --git a/library/core/src/num/dec2flt/parse.rs b/library/core/src/num/dec2flt/parse.rs
index 06ee8e95fbc..e38fedc58be 100644
--- a/library/core/src/num/dec2flt/parse.rs
+++ b/library/core/src/num/dec2flt/parse.rs
@@ -1,8 +1,8 @@
 //! Functions to parse floating-point numbers.
 
 use crate::num::dec2flt::common::{ByteSlice, is_8digits};
+use crate::num::dec2flt::decimal::Decimal;
 use crate::num::dec2flt::float::RawFloat;
-use crate::num::dec2flt::number::Number;
 
 const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000;
 
@@ -100,7 +100,7 @@ fn parse_scientific(s_ref: &mut &[u8]) -> Option<i64> {
 ///
 /// This creates a representation of the float as the
 /// significant digits and the decimal exponent.
-fn parse_partial_number(mut s: &[u8]) -> Option<(Number, usize)> {
+fn parse_partial_number(mut s: &[u8]) -> Option<(Decimal, usize)> {
     debug_assert!(!s.is_empty());
 
     // parse initial digits before dot
@@ -146,7 +146,7 @@ fn parse_partial_number(mut s: &[u8]) -> Option<(Number, usize)> {
 
     // handle uncommon case with many digits
     if n_digits <= 19 {
-        return Some((Number { exponent, mantissa, negative: false, many_digits: false }, len));
+        return Some((Decimal { exponent, mantissa, negative: false, many_digits: false }, len));
     }
 
     n_digits -= 19;
@@ -179,13 +179,13 @@ fn parse_partial_number(mut s: &[u8]) -> Option<(Number, usize)> {
         exponent += exp_number;
     }
 
-    Some((Number { exponent, mantissa, negative: false, many_digits }, len))
+    Some((Decimal { exponent, mantissa, negative: false, many_digits }, len))
 }
 
 /// Try to parse a non-special floating point number,
 /// as well as two slices with integer and fractional parts
 /// and the parsed exponent.
-pub fn parse_number(s: &[u8]) -> Option<Number> {
+pub fn parse_number(s: &[u8]) -> Option<Decimal> {
     if let Some((float, rest)) = parse_partial_number(s) {
         if rest == s.len() {
             return Some(float);
diff --git a/library/core/src/num/dec2flt/slow.rs b/library/core/src/num/dec2flt/slow.rs
index 85d4b13284b..3baed426523 100644
--- a/library/core/src/num/dec2flt/slow.rs
+++ b/library/core/src/num/dec2flt/slow.rs
@@ -1,7 +1,7 @@
 //! Slow, fallback algorithm for cases the Eisel-Lemire algorithm cannot round.
 
 use crate::num::dec2flt::common::BiasedFp;
-use crate::num::dec2flt::decimal::{Decimal, parse_decimal};
+use crate::num::dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq};
 use crate::num::dec2flt::float::RawFloat;
 
 /// Parse the significant digits and biased, binary exponent of a float.
@@ -36,7 +36,7 @@ pub(crate) fn parse_long_mantissa<F: RawFloat>(s: &[u8]) -> BiasedFp {
     let fp_zero = BiasedFp::zero_pow2(0);
     let fp_inf = BiasedFp::zero_pow2(F::INFINITE_POWER);
 
-    let mut d = parse_decimal(s);
+    let mut d = parse_decimal_seq(s);
 
     // Short-circuit if the value can only be a literal 0 or infinity.
     if d.num_digits == 0 || d.decimal_point < -324 {
@@ -50,7 +50,7 @@ pub(crate) fn parse_long_mantissa<F: RawFloat>(s: &[u8]) -> BiasedFp {
         let n = d.decimal_point as usize;
         let shift = get_shift(n);
         d.right_shift(shift);
-        if d.decimal_point < -Decimal::DECIMAL_POINT_RANGE {
+        if d.decimal_point < -DecimalSeq::DECIMAL_POINT_RANGE {
             return fp_zero;
         }
         exp2 += shift as i32;
@@ -67,43 +67,43 @@ pub(crate) fn parse_long_mantissa<F: RawFloat>(s: &[u8]) -> BiasedFp {
             get_shift((-d.decimal_point) as _)
         };
         d.left_shift(shift);
-        if d.decimal_point > Decimal::DECIMAL_POINT_RANGE {
+        if d.decimal_point > DecimalSeq::DECIMAL_POINT_RANGE {
             return fp_inf;
         }
         exp2 -= shift as i32;
     }
     // We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2].
     exp2 -= 1;
-    while (F::MINIMUM_EXPONENT + 1) > exp2 {
-        let mut n = ((F::MINIMUM_EXPONENT + 1) - exp2) as usize;
+    while F::EXP_MIN > exp2 {
+        let mut n = (F::EXP_MIN - exp2) as usize;
         if n > MAX_SHIFT {
             n = MAX_SHIFT;
         }
         d.right_shift(n);
         exp2 += n as i32;
     }
-    if (exp2 - F::MINIMUM_EXPONENT) >= F::INFINITE_POWER {
+    if (exp2 - F::EXP_MIN + 1) >= F::INFINITE_POWER {
         return fp_inf;
     }
     // Shift the decimal to the hidden bit, and then round the value
     // to get the high mantissa+1 bits.
-    d.left_shift(F::MANTISSA_EXPLICIT_BITS + 1);
+    d.left_shift(F::SIG_BITS as usize + 1);
     let mut mantissa = d.round();
-    if mantissa >= (1_u64 << (F::MANTISSA_EXPLICIT_BITS + 1)) {
+    if mantissa >= (1_u64 << (F::SIG_BITS + 1)) {
         // Rounding up overflowed to the carry bit, need to
         // shift back to the hidden bit.
         d.right_shift(1);
         exp2 += 1;
         mantissa = d.round();
-        if (exp2 - F::MINIMUM_EXPONENT) >= F::INFINITE_POWER {
+        if (exp2 - F::EXP_MIN + 1) >= F::INFINITE_POWER {
             return fp_inf;
         }
     }
-    let mut power2 = exp2 - F::MINIMUM_EXPONENT;
-    if mantissa < (1_u64 << F::MANTISSA_EXPLICIT_BITS) {
+    let mut power2 = exp2 - F::EXP_MIN + 1;
+    if mantissa < (1_u64 << F::SIG_BITS) {
         power2 -= 1;
     }
     // Zero out all the bits above the explicit mantissa bits.
-    mantissa &= (1_u64 << F::MANTISSA_EXPLICIT_BITS) - 1;
-    BiasedFp { f: mantissa, e: power2 }
+    mantissa &= (1_u64 << F::SIG_BITS) - 1;
+    BiasedFp { m: mantissa, p_biased: power2 }
 }
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index a200fd53186..de1557ccc90 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -493,13 +493,13 @@ impl f32 {
     pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32;
 
     /// Sign bit
-    const SIGN_MASK: u32 = 0x8000_0000;
+    pub(crate) const SIGN_MASK: u32 = 0x8000_0000;
 
     /// Exponent mask
-    const EXP_MASK: u32 = 0x7f80_0000;
+    pub(crate) const EXP_MASK: u32 = 0x7f80_0000;
 
     /// Mantissa mask
-    const MAN_MASK: u32 = 0x007f_ffff;
+    pub(crate) const MAN_MASK: u32 = 0x007f_ffff;
 
     /// Minimum representable positive value (min subnormal)
     const TINY_BITS: u32 = 0x1;
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index de63a462b61..65b5f3b9af0 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -492,13 +492,13 @@ impl f64 {
     pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64;
 
     /// Sign bit
-    const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
+    pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
 
     /// Exponent mask
-    const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
+    pub(crate) const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
 
     /// Mantissa mask
-    const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff;
+    pub(crate) const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff;
 
     /// Minimum representable positive value (min subnormal)
     const TINY_BITS: u64 = 0x1;
diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs
index 752e79c2dac..f8826096df3 100644
--- a/library/core/src/pat.rs
+++ b/library/core/src/pat.rs
@@ -12,3 +12,65 @@ macro_rules! pattern_type {
         /* compiler built-in */
     };
 }
+
+/// A trait implemented for integer types and `char`.
+/// Useful in the future for generic pattern types, but
+/// used right now to simplify ast lowering of pattern type ranges.
+#[unstable(feature = "pattern_type_range_trait", issue = "123646")]
+#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
+#[const_trait]
+#[diagnostic::on_unimplemented(
+    message = "`{Self}` is not a valid base type for range patterns",
+    label = "only integer types and `char` are supported"
+)]
+pub trait RangePattern {
+    /// Trait version of the inherent `MIN` assoc const.
+    #[cfg_attr(not(bootstrap), lang = "RangeMin")]
+    const MIN: Self;
+
+    /// Trait version of the inherent `MIN` assoc const.
+    #[cfg_attr(not(bootstrap), lang = "RangeMax")]
+    const MAX: Self;
+
+    /// A compile-time helper to subtract 1 for exclusive ranges.
+    #[cfg_attr(not(bootstrap), lang = "RangeSub")]
+    #[track_caller]
+    fn sub_one(self) -> Self;
+}
+
+macro_rules! impl_range_pat {
+    ($($ty:ty,)*) => {
+        $(
+            #[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
+            impl const RangePattern for $ty {
+                const MIN: $ty = <$ty>::MIN;
+                const MAX: $ty = <$ty>::MAX;
+                fn sub_one(self) -> Self {
+                    match self.checked_sub(1) {
+                        Some(val) => val,
+                        None => panic!("exclusive range end at minimum value of type")
+                    }
+                }
+            }
+        )*
+    }
+}
+
+impl_range_pat! {
+    i8, i16, i32, i64, i128, isize,
+    u8, u16, u32, u64, u128, usize,
+}
+
+#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
+impl const RangePattern for char {
+    const MIN: Self = char::MIN;
+
+    const MAX: Self = char::MAX;
+
+    fn sub_one(self) -> Self {
+        match char::from_u32(self as u32 - 1) {
+            None => panic!("exclusive range to start of valid chars"),
+            Some(val) => val,
+        }
+    }
+}
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index c79ae2c1915..3570d8d0876 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -78,7 +78,7 @@ pub use raw::{from_raw_parts, from_raw_parts_mut};
 
 /// Calculates the direction and split point of a one-sided range.
 ///
-/// This is a helper function for `take` and `take_mut` that returns
+/// This is a helper function for `split_off` and `split_off_mut` that returns
 /// the direction of the split (front or back) as well as the index at
 /// which to split. Returns `None` if the split index would overflow.
 #[inline]
@@ -3732,8 +3732,7 @@ impl<T> [T] {
     #[doc(alias = "memcpy")]
     #[inline]
     #[stable(feature = "copy_from_slice", since = "1.9.0")]
-    #[rustc_const_unstable(feature = "const_copy_from_slice", issue = "131415")]
-    #[rustc_const_stable_indirect]
+    #[rustc_const_stable(feature = "const_copy_from_slice", since = "CURRENT_RUSTC_VERSION")]
     #[track_caller]
     pub const fn copy_from_slice(&mut self, src: &[T])
     where
@@ -4313,8 +4312,6 @@ impl<T> [T] {
     /// Splitting off the first three elements of a slice:
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &[_] = &['a', 'b', 'c', 'd'];
     /// let mut first_three = slice.split_off(..3).unwrap();
     ///
@@ -4325,8 +4322,6 @@ impl<T> [T] {
     /// Splitting off the last two elements of a slice:
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &[_] = &['a', 'b', 'c', 'd'];
     /// let mut tail = slice.split_off(2..).unwrap();
     ///
@@ -4337,8 +4332,6 @@ impl<T> [T] {
     /// Getting `None` when `range` is out of bounds:
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &[_] = &['a', 'b', 'c', 'd'];
     ///
     /// assert_eq!(None, slice.split_off(5..));
@@ -4349,7 +4342,7 @@ impl<T> [T] {
     /// ```
     #[inline]
     #[must_use = "method does not modify the slice if the range is out of bounds"]
-    #[unstable(feature = "slice_take", issue = "62280")]
+    #[stable(feature = "slice_take", since = "CURRENT_RUSTC_VERSION")]
     pub fn split_off<'a, R: OneSidedRange<usize>>(
         self: &mut &'a Self,
         range: R,
@@ -4385,8 +4378,6 @@ impl<T> [T] {
     /// Splitting off the first three elements of a slice:
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
     /// let mut first_three = slice.split_off_mut(..3).unwrap();
     ///
@@ -4397,8 +4388,6 @@ impl<T> [T] {
     /// Taking the last two elements of a slice:
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
     /// let mut tail = slice.split_off_mut(2..).unwrap();
     ///
@@ -4409,8 +4398,6 @@ impl<T> [T] {
     /// Getting `None` when `range` is out of bounds:
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
     ///
     /// assert_eq!(None, slice.split_off_mut(5..));
@@ -4421,7 +4408,7 @@ impl<T> [T] {
     /// ```
     #[inline]
     #[must_use = "method does not modify the slice if the range is out of bounds"]
-    #[unstable(feature = "slice_take", issue = "62280")]
+    #[stable(feature = "slice_take", since = "CURRENT_RUSTC_VERSION")]
     pub fn split_off_mut<'a, R: OneSidedRange<usize>>(
         self: &mut &'a mut Self,
         range: R,
@@ -4451,8 +4438,6 @@ impl<T> [T] {
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &[_] = &['a', 'b', 'c'];
     /// let first = slice.split_off_first().unwrap();
     ///
@@ -4460,7 +4445,7 @@ impl<T> [T] {
     /// assert_eq!(first, &'a');
     /// ```
     #[inline]
-    #[unstable(feature = "slice_take", issue = "62280")]
+    #[stable(feature = "slice_take", since = "CURRENT_RUSTC_VERSION")]
     pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> {
         let (first, rem) = self.split_first()?;
         *self = rem;
@@ -4475,8 +4460,6 @@ impl<T> [T] {
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
     /// let first = slice.split_off_first_mut().unwrap();
     /// *first = 'd';
@@ -4485,7 +4468,7 @@ impl<T> [T] {
     /// assert_eq!(first, &'d');
     /// ```
     #[inline]
-    #[unstable(feature = "slice_take", issue = "62280")]
+    #[stable(feature = "slice_take", since = "CURRENT_RUSTC_VERSION")]
     pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> {
         let (first, rem) = mem::take(self).split_first_mut()?;
         *self = rem;
@@ -4500,8 +4483,6 @@ impl<T> [T] {
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &[_] = &['a', 'b', 'c'];
     /// let last = slice.split_off_last().unwrap();
     ///
@@ -4509,7 +4490,7 @@ impl<T> [T] {
     /// assert_eq!(last, &'c');
     /// ```
     #[inline]
-    #[unstable(feature = "slice_take", issue = "62280")]
+    #[stable(feature = "slice_take", since = "CURRENT_RUSTC_VERSION")]
     pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> {
         let (last, rem) = self.split_last()?;
         *self = rem;
@@ -4524,8 +4505,6 @@ impl<T> [T] {
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_take)]
-    ///
     /// let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
     /// let last = slice.split_off_last_mut().unwrap();
     /// *last = 'd';
@@ -4534,7 +4513,7 @@ impl<T> [T] {
     /// assert_eq!(last, &'d');
     /// ```
     #[inline]
-    #[unstable(feature = "slice_take", issue = "62280")]
+    #[stable(feature = "slice_take", since = "CURRENT_RUSTC_VERSION")]
     pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> {
         let (last, rem) = mem::take(self).split_last_mut()?;
         *self = rem;