From bec94a33d541667abb706e8bab4dc791e64e87cc Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 31 Jan 2026 03:13:08 -0600 Subject: [PATCH] dec2flt: Move internal traits to better locations `Float` and `FloatExt` are already used by both parsing and printing, so move them out of `dec2flt` to a new module in `num::imp`. `Int` `Cast` have the potential to be used more places in the future, so move them there as well. `Lemire` is the only remaining trait; since it is small, move it into the `dec2flt` root. The `fmt::LowerExp` bound is removed from `Float` here since the trait is moving into a module without `#[cfg(not(no_fp_fmt_parse))]` and it isn't implemented with that config (it's not easily possible to add `cfg` attributes to a single supertrait, unfortunately). This isn't a problem since it isn't actually being used. --- library/core/src/num/imp/dec2flt/decimal.rs | 5 +- library/core/src/num/imp/dec2flt/lemire.rs | 3 +- library/core/src/num/imp/dec2flt/mod.rs | 90 ++++++++++++++- library/core/src/num/imp/dec2flt/parse.rs | 3 +- library/core/src/num/imp/dec2flt/slow.rs | 3 +- library/core/src/num/imp/flt2dec/decoder.rs | 2 +- library/core/src/num/imp/mod.rs | 3 + .../num/imp/{dec2flt/float.rs => traits.rs} | 106 +++--------------- library/coretests/tests/lib.rs | 1 + library/coretests/tests/num/dec2flt/float.rs | 3 +- library/coretests/tests/num/dec2flt/lemire.rs | 3 +- 11 files changed, 113 insertions(+), 109 deletions(-) rename library/core/src/num/imp/{dec2flt/float.rs => traits.rs} (74%) diff --git a/library/core/src/num/imp/dec2flt/decimal.rs b/library/core/src/num/imp/dec2flt/decimal.rs index 583f6bcf3302..c9b0cc531b38 100644 --- a/library/core/src/num/imp/dec2flt/decimal.rs +++ b/library/core/src/num/imp/dec2flt/decimal.rs @@ -1,9 +1,10 @@ //! Representation of a float as the significant digits and exponent. -use crate::num::imp::dec2flt; -use dec2flt::float::Lemire; +use dec2flt::Lemire; use dec2flt::fpu::set_precision; +use crate::num::imp::dec2flt; + const INT_POW10: [u64; 16] = [ 1, 10, diff --git a/library/core/src/num/imp/dec2flt/lemire.rs b/library/core/src/num/imp/dec2flt/lemire.rs index 8a97618cf738..f89d16c84347 100644 --- a/library/core/src/num/imp/dec2flt/lemire.rs +++ b/library/core/src/num/imp/dec2flt/lemire.rs @@ -1,10 +1,9 @@ //! Implementation of the Eisel-Lemire algorithm. use dec2flt::common::BiasedFp; -use dec2flt::float::Float; use dec2flt::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE}; -use crate::num::imp::dec2flt; +use crate::num::imp::{Float, dec2flt}; /// Compute w * 10^q using an extended-precision float representation. /// diff --git a/library/core/src/num/imp/dec2flt/mod.rs b/library/core/src/num/imp/dec2flt/mod.rs index 33e606694bc3..76b8d416ee0f 100644 --- a/library/core/src/num/imp/dec2flt/mod.rs +++ b/library/core/src/num/imp/dec2flt/mod.rs @@ -88,24 +88,104 @@ )] use common::BiasedFp; -use float::{FloatExt, Lemire}; use lemire::compute_float; use parse::{parse_inf_nan, parse_number}; use slow::parse_long_mantissa; +use crate::f64; use crate::num::ParseFloatError; use crate::num::float_parse::FloatErrorKind; +use crate::num::imp::FloatExt; mod common; 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 parse; +mod slow; +mod table; + +/// Extension to `Float` that are necessary for parsing using the Lemire method. +/// +/// See the parent module's doc comment for why this is necessary. +/// +/// Not intended for use outside of the `dec2flt` module. +#[doc(hidden)] +pub trait Lemire: FloatExt { + /// 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 + }; + + /// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋` + const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH; + + /// 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 = 1 << Self::SIG_TOTAL_BITS; + + /// Gets a small power-of-ten for fast-path multiplication. + fn pow10_fast_path(exponent: usize) -> Self; + + /// Converts integer into float through an as cast. + /// This is only called in the fast-path algorithm, and therefore + /// will not lose precision, since the value will always have + /// only if the value is <= Self::MAX_MANTISSA_FAST_PATH. + fn from_u64(v: u64) -> Self; +} + +#[cfg(target_has_reliable_f16)] +impl Lemire for f16 { + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; + TABLE[exponent & 7] + } + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } +} + +impl Lemire for f32 { + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f32; 16] = + [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.]; + TABLE[exponent & 15] + } + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } +} + +impl Lemire for f64 { + fn pow10_fast_path(exponent: usize) -> Self { + const TABLE: [f64; 32] = [ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, + 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0., + ]; + TABLE[exponent & 31] + } + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } +} #[inline] pub(super) fn pfe_empty() -> ParseFloatError { diff --git a/library/core/src/num/imp/dec2flt/parse.rs b/library/core/src/num/imp/dec2flt/parse.rs index e4e3e5bc0c0e..ee55eadbc7b9 100644 --- a/library/core/src/num/imp/dec2flt/parse.rs +++ b/library/core/src/num/imp/dec2flt/parse.rs @@ -2,9 +2,8 @@ use dec2flt::common::{ByteSlice, is_8digits}; use dec2flt::decimal::Decimal; -use dec2flt::float::Float; -use crate::num::imp::dec2flt; +use crate::num::imp::{Float, dec2flt}; const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000; diff --git a/library/core/src/num/imp/dec2flt/slow.rs b/library/core/src/num/imp/dec2flt/slow.rs index 784f3e00f5b4..f1b2525cf38e 100644 --- a/library/core/src/num/imp/dec2flt/slow.rs +++ b/library/core/src/num/imp/dec2flt/slow.rs @@ -2,9 +2,8 @@ use dec2flt::common::BiasedFp; use dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; -use dec2flt::float::Float; -use crate::num::imp::dec2flt; +use crate::num::imp::{Float, dec2flt}; /// Parse the significant digits and biased, binary exponent of a float. /// diff --git a/library/core/src/num/imp/flt2dec/decoder.rs b/library/core/src/num/imp/flt2dec/decoder.rs index 6f8fb2b954f3..5ccc91f4c9fa 100644 --- a/library/core/src/num/imp/flt2dec/decoder.rs +++ b/library/core/src/num/imp/flt2dec/decoder.rs @@ -1,7 +1,7 @@ //! Decodes a floating-point value into individual parts and error ranges. use crate::num::FpCategory; -use crate::num::imp::dec2flt::float::FloatExt; +use crate::num::imp::FloatExt; /// Decoded unsigned finite value, such that: /// diff --git a/library/core/src/num/imp/mod.rs b/library/core/src/num/imp/mod.rs index d35409f91bde..6fccfd1c238e 100644 --- a/library/core/src/num/imp/mod.rs +++ b/library/core/src/num/imp/mod.rs @@ -16,3 +16,6 @@ pub(crate) mod int_sqrt; pub(crate) mod libm; pub(crate) mod overflow_panic; +mod traits; + +pub use traits::{Float, FloatExt, Int}; diff --git a/library/core/src/num/imp/dec2flt/float.rs b/library/core/src/num/imp/traits.rs similarity index 74% rename from library/core/src/num/imp/dec2flt/float.rs rename to library/core/src/num/imp/traits.rs index da581a457f49..7b84f7a4a5aa 100644 --- a/library/core/src/num/imp/dec2flt/float.rs +++ b/library/core/src/num/imp/traits.rs @@ -1,10 +1,14 @@ -//! Helper trait for generic float types. +//! Numeric traits used for internal implementations. -use core::f64; +#![doc(hidden)] +#![unstable( + feature = "num_internals", + reason = "internal routines only exposed for testing", + issue = "none" +)] -use crate::fmt::{Debug, LowerExp}; use crate::num::FpCategory; -use crate::ops::{self, Add, Div, Mul, Neg}; +use crate::{f64, fmt, ops}; /// Lossy `as` casting between two types. pub trait CastInto: Copy { @@ -16,7 +20,7 @@ pub trait Int: Sized + Clone + Copy - + Debug + + fmt::Debug + ops::Shr + ops::Shl + ops::BitAnd @@ -51,17 +55,16 @@ impl Int for $ty { #[doc(hidden)] pub trait Float: Sized - + Div - + Neg - + Mul - + Add - + LowerExp + + ops::Div + + ops::Neg + + ops::Mul + + ops::Add + + fmt::Debug + PartialEq + PartialOrd + Default + Clone + Copy - + Debug { /// The unsigned integer with the same size as the float type Int: Int + Into; @@ -184,41 +187,6 @@ fn integer_decode(self) -> (u64, i16, i8) { } } -/// Extension to `Float` that are necessary for parsing using the Lemire method. -/// -/// See the parent module's doc comment for why this is necessary. -/// -/// Not intended for use outside of the `dec2flt` module. -#[doc(hidden)] -pub trait Lemire: FloatExt { - /// 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 - }; - - /// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋` - const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH; - - /// 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 = 1 << Self::SIG_TOTAL_BITS; - - /// Gets a small power-of-ten for fast-path multiplication. - fn pow10_fast_path(exponent: usize) -> Self; - - /// Converts integer into float through an as cast. - /// This is only called in the fast-path algorithm, and therefore - /// will not lose precision, since the value will always have - /// only if the value is <= Self::MAX_MANTISSA_FAST_PATH. - fn from_u64(v: u64) -> Self; -} - /// 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; @@ -260,21 +228,6 @@ fn from_u64_bits(v: u64) -> Self { } } -#[cfg(target_has_reliable_f16)] -impl Lemire for f16 { - fn pow10_fast_path(exponent: usize) -> Self { - #[allow(clippy::use_self)] - const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; - TABLE[exponent & 7] - } - - #[inline] - fn from_u64(v: u64) -> Self { - debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); - v as _ - } -} - impl Float for f32 { type Int = u32; @@ -308,21 +261,6 @@ fn from_u64_bits(v: u64) -> Self { } } -impl Lemire for f32 { - fn pow10_fast_path(exponent: usize) -> Self { - #[allow(clippy::use_self)] - const TABLE: [f32; 16] = - [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.]; - TABLE[exponent & 15] - } - - #[inline] - fn from_u64(v: u64) -> Self { - debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); - v as _ - } -} - impl Float for f64 { type Int = u64; @@ -355,19 +293,3 @@ fn from_u64_bits(v: u64) -> Self { f64::from_bits(v) } } - -impl Lemire for f64 { - fn pow10_fast_path(exponent: usize) -> Self { - const TABLE: [f64; 32] = [ - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, - 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0., - ]; - TABLE[exponent & 31] - } - - #[inline] - fn from_u64(v: u64) -> Self { - debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); - v as _ - } -} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 72112f8b0113..b3fd53cbb9dc 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -88,6 +88,7 @@ #![feature(next_index)] #![feature(non_exhaustive_omitted_patterns_lint)] #![feature(nonzero_from_str_radix)] +#![feature(num_internals)] #![feature(numfmt)] #![feature(one_sided_range)] #![feature(panic_internals)] diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index aa4c59dd66ea..0713c5c651fb 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -1,4 +1,5 @@ -use core::num::imp::dec2flt::float::{Float, FloatExt, Lemire}; +use core::num::imp::dec2flt::Lemire; +use core::num::imp::{Float, FloatExt}; use crate::num::{ldexp_f32, ldexp_f64}; diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index 6679dca148aa..e5a7ae346f42 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -1,6 +1,5 @@ -use core::num::imp::dec2flt; +use core::num::imp::{Float, dec2flt}; -use dec2flt::float::Float; use dec2flt::lemire::compute_float; #[cfg(target_has_reliable_f16)]