Skip to content

Commit c5903c5

Browse files
Rollup merge of rust-lang#151905 - tgross35:dec2flt-traits, r=Mark-Simulacrum
Split the `dec2flt::RawFloat` trait for easier reuse `RawFloat` is an internal trait with quite a few useful float properties. It currently resides in the `dec2flt` module but is also used in parsing, and would be nice to reuse other places. Unfortunately it cannot be implemented on `f128` because of limitations with how the parsing API is implemented (mantissa must fit into a u64). To make the trait easier to work with, split it into the following: * `Float`: Anything that is reasonably applicable to all floating point types. * `FloatExt`: Items that should be part of `Float` but don't work for all float types. This will eventually be merged back into `Float` once it can be implemented on f128. * `Lemire`: Items that are specific to the Lemire dec2flt algorithm. These traits are then moved to places that make sense. Builds on top of rust-lang#151900
2 parents 78f7412 + bec94a3 commit c5903c5

11 files changed

Lines changed: 204 additions & 177 deletions

File tree

library/core/src/num/imp/dec2flt/decimal.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Representation of a float as the significant digits and exponent.
22
3-
use dec2flt::float::RawFloat;
3+
use dec2flt::Lemire;
44
use dec2flt::fpu::set_precision;
55

66
use crate::num::imp::dec2flt;
@@ -36,7 +36,7 @@ pub struct Decimal {
3636
impl Decimal {
3737
/// Detect if the float can be accurately reconstructed from native floats.
3838
#[inline]
39-
fn can_use_fast_path<F: RawFloat>(&self) -> bool {
39+
fn can_use_fast_path<F: Lemire>(&self) -> bool {
4040
F::MIN_EXPONENT_FAST_PATH <= self.exponent
4141
&& self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH
4242
&& self.mantissa <= F::MAX_MANTISSA_FAST_PATH
@@ -53,7 +53,7 @@ impl Decimal {
5353
///
5454
/// There is an exception: disguised fast-path cases, where we can shift
5555
/// powers-of-10 from the exponent to the significant digits.
56-
pub fn try_fast_path<F: RawFloat>(&self) -> Option<F> {
56+
pub fn try_fast_path<F: Lemire>(&self) -> Option<F> {
5757
// Here we need to work around <https://github.com/rust-lang/rust/issues/114479>.
5858
// The fast path crucially depends on arithmetic being rounded to the correct number of bits
5959
// without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision

library/core/src/num/imp/dec2flt/lemire.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
//! Implementation of the Eisel-Lemire algorithm.
22
33
use dec2flt::common::BiasedFp;
4-
use dec2flt::float::RawFloat;
54
use dec2flt::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE};
65

7-
use crate::num::imp::dec2flt;
6+
use crate::num::imp::{Float, dec2flt};
87

98
/// Compute w * 10^q using an extended-precision float representation.
109
///
@@ -24,7 +23,7 @@ use crate::num::imp::dec2flt;
2423
/// at a Gigabyte per Second" in section 5, "Fast Algorithm", and
2524
/// section 6, "Exact Numbers And Ties", available online:
2625
/// <https://arxiv.org/abs/2101.11408.pdf>.
27-
pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
26+
pub fn compute_float<F: Float>(q: i64, mut w: u64) -> BiasedFp {
2827
let fp_zero = BiasedFp::zero_pow2(0);
2928
let fp_inf = BiasedFp::zero_pow2(F::INFINITE_POWER);
3029
let fp_error = BiasedFp::zero_pow2(-1);

library/core/src/num/imp/dec2flt/mod.rs

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,24 +88,104 @@
8888
)]
8989

9090
use common::BiasedFp;
91-
use float::RawFloat;
9291
use lemire::compute_float;
9392
use parse::{parse_inf_nan, parse_number};
9493
use slow::parse_long_mantissa;
9594

95+
use crate::f64;
9696
use crate::num::ParseFloatError;
9797
use crate::num::float_parse::FloatErrorKind;
98+
use crate::num::imp::FloatExt;
9899

99100
mod common;
100101
pub mod decimal;
101102
pub mod decimal_seq;
102103
mod fpu;
103-
mod slow;
104-
mod table;
105-
// float is used in flt2dec, and all are used in unit tests.
106-
pub mod float;
107104
pub mod lemire;
108105
pub mod parse;
106+
mod slow;
107+
mod table;
108+
109+
/// Extension to `Float` that are necessary for parsing using the Lemire method.
110+
///
111+
/// See the parent module's doc comment for why this is necessary.
112+
///
113+
/// Not intended for use outside of the `dec2flt` module.
114+
#[doc(hidden)]
115+
pub trait Lemire: FloatExt {
116+
/// Maximum exponent for a fast path case, or `⌊(SIG_BITS+1)/log2(5)⌋`
117+
// assuming FLT_EVAL_METHOD = 0
118+
const MAX_EXPONENT_FAST_PATH: i64 = {
119+
let log2_5 = f64::consts::LOG2_10 - 1.0;
120+
(Self::SIG_TOTAL_BITS as f64 / log2_5) as i64
121+
};
122+
123+
/// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋`
124+
const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH;
125+
126+
/// Maximum exponent that can be represented for a disguised-fast path case.
127+
/// This is `MAX_EXPONENT_FAST_PATH + ⌊(SIG_BITS+1)/log2(10)⌋`
128+
const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 =
129+
Self::MAX_EXPONENT_FAST_PATH + (Self::SIG_TOTAL_BITS as f64 / f64::consts::LOG2_10) as i64;
130+
131+
/// Maximum mantissa for the fast-path (`1 << 53` for f64).
132+
const MAX_MANTISSA_FAST_PATH: u64 = 1 << Self::SIG_TOTAL_BITS;
133+
134+
/// Gets a small power-of-ten for fast-path multiplication.
135+
fn pow10_fast_path(exponent: usize) -> Self;
136+
137+
/// Converts integer into float through an as cast.
138+
/// This is only called in the fast-path algorithm, and therefore
139+
/// will not lose precision, since the value will always have
140+
/// only if the value is <= Self::MAX_MANTISSA_FAST_PATH.
141+
fn from_u64(v: u64) -> Self;
142+
}
143+
144+
#[cfg(target_has_reliable_f16)]
145+
impl Lemire for f16 {
146+
fn pow10_fast_path(exponent: usize) -> Self {
147+
#[allow(clippy::use_self)]
148+
const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.];
149+
TABLE[exponent & 7]
150+
}
151+
152+
#[inline]
153+
fn from_u64(v: u64) -> Self {
154+
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
155+
v as _
156+
}
157+
}
158+
159+
impl Lemire for f32 {
160+
fn pow10_fast_path(exponent: usize) -> Self {
161+
#[allow(clippy::use_self)]
162+
const TABLE: [f32; 16] =
163+
[1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.];
164+
TABLE[exponent & 15]
165+
}
166+
167+
#[inline]
168+
fn from_u64(v: u64) -> Self {
169+
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
170+
v as _
171+
}
172+
}
173+
174+
impl Lemire for f64 {
175+
fn pow10_fast_path(exponent: usize) -> Self {
176+
const TABLE: [f64; 32] = [
177+
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
178+
1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0.,
179+
];
180+
TABLE[exponent & 31]
181+
}
182+
183+
#[inline]
184+
fn from_u64(v: u64) -> Self {
185+
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
186+
v as _
187+
}
188+
}
109189

110190
#[inline]
111191
pub(super) fn pfe_empty() -> ParseFloatError {
@@ -120,15 +200,15 @@ pub fn pfe_invalid() -> ParseFloatError {
120200
}
121201

122202
/// Converts a `BiasedFp` to the closest machine float type.
123-
fn biased_fp_to_float<F: RawFloat>(x: BiasedFp) -> F {
203+
fn biased_fp_to_float<F: FloatExt>(x: BiasedFp) -> F {
124204
let mut word = x.m;
125205
word |= (x.p_biased as u64) << F::SIG_BITS;
126206
F::from_u64_bits(word)
127207
}
128208

129209
/// Converts a decimal string into a floating point number.
130210
#[inline(always)] // Will be inlined into a function with `#[inline(never)]`, see above
131-
pub fn dec2flt<F: RawFloat>(s: &str) -> Result<F, ParseFloatError> {
211+
pub fn dec2flt<F: Lemire>(s: &str) -> Result<F, ParseFloatError> {
132212
let mut s = s.as_bytes();
133213
let Some(&c) = s.first() else { return Err(pfe_empty()) };
134214
let negative = c == b'-';

library/core/src/num/imp/dec2flt/parse.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
33
use dec2flt::common::{ByteSlice, is_8digits};
44
use dec2flt::decimal::Decimal;
5-
use dec2flt::float::RawFloat;
65

7-
use crate::num::imp::dec2flt;
6+
use crate::num::imp::{Float, dec2flt};
87

98
const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000;
109

@@ -197,7 +196,7 @@ pub fn parse_number(s: &[u8]) -> Option<Decimal> {
197196
}
198197

199198
/// Try to parse a special, non-finite float.
200-
pub(crate) fn parse_inf_nan<F: RawFloat>(s: &[u8], negative: bool) -> Option<F> {
199+
pub(crate) fn parse_inf_nan<F: Float>(s: &[u8], negative: bool) -> Option<F> {
201200
// Since a valid string has at most the length 8, we can load
202201
// all relevant characters into a u64 and work from there.
203202
// This also generates much better code.

library/core/src/num/imp/dec2flt/slow.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
33
use dec2flt::common::BiasedFp;
44
use dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq};
5-
use dec2flt::float::RawFloat;
65

7-
use crate::num::imp::dec2flt;
6+
use crate::num::imp::{Float, dec2flt};
87

98
/// Parse the significant digits and biased, binary exponent of a float.
109
///
@@ -25,7 +24,7 @@ use crate::num::imp::dec2flt;
2524
///
2625
/// The algorithms described here are based on "Processing Long Numbers Quickly",
2726
/// available here: <https://arxiv.org/pdf/2101.11408.pdf#section.11>.
28-
pub(crate) fn parse_long_mantissa<F: RawFloat>(s: &[u8]) -> BiasedFp {
27+
pub(crate) fn parse_long_mantissa<F: Float>(s: &[u8]) -> BiasedFp {
2928
const MAX_SHIFT: usize = 60;
3029
const NUM_POWERS: usize = 19;
3130
const POWERS: [u8; 19] =

library/core/src/num/imp/flt2dec/decoder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Decodes a floating-point value into individual parts and error ranges.
22
33
use crate::num::FpCategory;
4-
use crate::num::imp::dec2flt::float::RawFloat;
4+
use crate::num::imp::FloatExt;
55

66
/// Decoded unsigned finite value, such that:
77
///
@@ -40,7 +40,7 @@ pub enum FullDecoded {
4040
}
4141

4242
/// A floating point type which can be `decode`d.
43-
pub trait DecodableFloat: RawFloat + Copy {
43+
pub trait DecodableFloat: FloatExt + Copy {
4444
/// The minimum positive normalized value.
4545
fn min_pos_norm_value() -> Self;
4646
}

library/core/src/num/imp/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ pub(crate) mod int_log10;
1616
pub(crate) mod int_sqrt;
1717
pub(crate) mod libm;
1818
pub(crate) mod overflow_panic;
19+
mod traits;
20+
21+
pub use traits::{Float, FloatExt, Int};

0 commit comments

Comments
 (0)