diff options
Diffstat (limited to 'library/core/src')
| -rw-r--r-- | library/core/src/bstr.rs | 4 | ||||
| -rw-r--r-- | library/core/src/convert/mod.rs | 1 | ||||
| -rw-r--r-- | library/core/src/fmt/mod.rs | 66 | ||||
| -rw-r--r-- | library/core/src/intrinsics/mod.rs | 2 | ||||
| -rw-r--r-- | library/core/src/iter/sources/successors.rs | 16 | ||||
| -rw-r--r-- | library/core/src/lib.rs | 3 | ||||
| -rw-r--r-- | library/core/src/marker.rs | 4 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/common.rs | 27 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/decimal.rs | 421 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/decimal_seq.rs | 379 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/float.rs | 272 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/lemire.rs | 22 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/mod.rs | 37 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/number.rs | 88 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/parse.rs | 10 | ||||
| -rw-r--r-- | library/core/src/num/dec2flt/slow.rs | 28 | ||||
| -rw-r--r-- | library/core/src/num/f32.rs | 6 | ||||
| -rw-r--r-- | library/core/src/num/f64.rs | 6 | ||||
| -rw-r--r-- | library/core/src/pat.rs | 62 | ||||
| -rw-r--r-- | library/core/src/slice/mod.rs | 37 |
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; |
