Skip to content

Commit 598219b

Browse files
committed
dec2flt: Split up the RawFloat trait
`RawFloat` is currently used specifically for the implementation of the lemire algorithm, but it is useful for more than that. Split it into three different traits: * `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`. * `Lemire`: Items that are specific to the Lemire algorithm.
1 parent 5241a2f commit 598219b

9 files changed

Lines changed: 156 additions & 133 deletions

File tree

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
//! Representation of a float as the significant digits and exponent.
22
3-
use dec2flt::float::RawFloat;
4-
use dec2flt::fpu::set_precision;
5-
63
use crate::num::imp::dec2flt;
4+
use dec2flt::float::Lemire;
5+
use dec2flt::fpu::set_precision;
76

87
const INT_POW10: [u64; 16] = [
98
1,
@@ -36,7 +35,7 @@ pub struct Decimal {
3635
impl Decimal {
3736
/// Detect if the float can be accurately reconstructed from native floats.
3837
#[inline]
39-
fn can_use_fast_path<F: RawFloat>(&self) -> bool {
38+
fn can_use_fast_path<F: Lemire>(&self) -> bool {
4039
F::MIN_EXPONENT_FAST_PATH <= self.exponent
4140
&& self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH
4241
&& self.mantissa <= F::MAX_MANTISSA_FAST_PATH
@@ -53,7 +52,7 @@ impl Decimal {
5352
///
5453
/// There is an exception: disguised fast-path cases, where we can shift
5554
/// powers-of-10 from the exponent to the significant digits.
56-
pub fn try_fast_path<F: RawFloat>(&self) -> Option<F> {
55+
pub fn try_fast_path<F: Lemire>(&self) -> Option<F> {
5756
// Here we need to work around <https://github.com/rust-lang/rust/issues/114479>.
5857
// The fast path crucially depends on arithmetic being rounded to the correct number of bits
5958
// without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision

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

Lines changed: 91 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,8 @@ macro_rules! int {
4848
int!(u16, u32, u64);
4949

5050
/// A helper trait to avoid duplicating basically all the conversion code for IEEE floats.
51-
///
52-
/// See the parent module's doc comment for why this is necessary.
53-
///
54-
/// Should **never ever** be implemented for other types or be used outside the `dec2flt` module.
5551
#[doc(hidden)]
56-
pub trait RawFloat:
52+
pub trait Float:
5753
Sized
5854
+ Div<Output = Self>
5955
+ Neg<Output = Self>
@@ -128,8 +124,6 @@ pub trait RawFloat:
128124
const MIN_EXPONENT_ROUND_TO_EVEN: i32;
129125
const MAX_EXPONENT_ROUND_TO_EVEN: i32;
130126

131-
/* limits related to Fast pathing */
132-
133127
/// Largest decimal exponent for a non-infinite value.
134128
///
135129
/// This is the max exponent in binary converted to the max exponent in decimal. Allows fast
@@ -151,41 +145,19 @@ pub trait RawFloat:
151145
/// compile time since intermediates exceed the range of an `f64`.
152146
const SMALLEST_POWER_OF_TEN: i32;
153147

154-
/// Maximum exponent for a fast path case, or `⌊(SIG_BITS+1)/log2(5)⌋`
155-
// assuming FLT_EVAL_METHOD = 0
156-
const MAX_EXPONENT_FAST_PATH: i64 = {
157-
let log2_5 = f64::consts::LOG2_10 - 1.0;
158-
(Self::SIG_TOTAL_BITS as f64 / log2_5) as i64
159-
};
160-
161-
/// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋`
162-
const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH;
163-
164-
/// Maximum exponent that can be represented for a disguised-fast path case.
165-
/// This is `MAX_EXPONENT_FAST_PATH + ⌊(SIG_BITS+1)/log2(10)⌋`
166-
const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 =
167-
Self::MAX_EXPONENT_FAST_PATH + (Self::SIG_TOTAL_BITS as f64 / f64::consts::LOG2_10) as i64;
168-
169-
/// Maximum mantissa for the fast-path (`1 << 53` for f64).
170-
const MAX_MANTISSA_FAST_PATH: u64 = 1 << Self::SIG_TOTAL_BITS;
171-
172-
/// Converts integer into float through an as cast.
173-
/// This is only called in the fast-path algorithm, and therefore
174-
/// will not lose precision, since the value will always have
175-
/// only if the value is <= Self::MAX_MANTISSA_FAST_PATH.
176-
fn from_u64(v: u64) -> Self;
177-
178-
/// Performs a raw transmutation from an integer.
179-
fn from_u64_bits(v: u64) -> Self;
180-
181-
/// Gets a small power-of-ten for fast-path multiplication.
182-
fn pow10_fast_path(exponent: usize) -> Self;
183-
184148
/// Returns the category that this number falls into.
185149
fn classify(self) -> FpCategory;
186150

187151
/// Transmute to the integer representation
188152
fn to_bits(self) -> Self::Int;
153+
}
154+
155+
/// Items that ideally would be on `Float`, but don't apply to all float types because they
156+
/// rely on the mantissa fitting into a `u64` (which isn't true for `f128`).
157+
#[doc(hidden)]
158+
pub trait FloatExt: Float {
159+
/// Performs a raw transmutation from an integer.
160+
fn from_u64_bits(v: u64) -> Self;
189161

190162
/// Returns the mantissa, exponent and sign as integers.
191163
///
@@ -212,14 +184,49 @@ pub trait RawFloat:
212184
}
213185
}
214186

187+
/// Extension to `Float` that are necessary for parsing using the Lemire method.
188+
///
189+
/// See the parent module's doc comment for why this is necessary.
190+
///
191+
/// Not intended for use outside of the `dec2flt` module.
192+
#[doc(hidden)]
193+
pub trait Lemire: FloatExt {
194+
/// Maximum exponent for a fast path case, or `⌊(SIG_BITS+1)/log2(5)⌋`
195+
// assuming FLT_EVAL_METHOD = 0
196+
const MAX_EXPONENT_FAST_PATH: i64 = {
197+
let log2_5 = f64::consts::LOG2_10 - 1.0;
198+
(Self::SIG_TOTAL_BITS as f64 / log2_5) as i64
199+
};
200+
201+
/// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋`
202+
const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH;
203+
204+
/// Maximum exponent that can be represented for a disguised-fast path case.
205+
/// This is `MAX_EXPONENT_FAST_PATH + ⌊(SIG_BITS+1)/log2(10)⌋`
206+
const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 =
207+
Self::MAX_EXPONENT_FAST_PATH + (Self::SIG_TOTAL_BITS as f64 / f64::consts::LOG2_10) as i64;
208+
209+
/// Maximum mantissa for the fast-path (`1 << 53` for f64).
210+
const MAX_MANTISSA_FAST_PATH: u64 = 1 << Self::SIG_TOTAL_BITS;
211+
212+
/// Gets a small power-of-ten for fast-path multiplication.
213+
fn pow10_fast_path(exponent: usize) -> Self;
214+
215+
/// Converts integer into float through an as cast.
216+
/// This is only called in the fast-path algorithm, and therefore
217+
/// will not lose precision, since the value will always have
218+
/// only if the value is <= Self::MAX_MANTISSA_FAST_PATH.
219+
fn from_u64(v: u64) -> Self;
220+
}
221+
215222
/// Solve for `b` in `10^b = 2^a`
216223
const fn pow2_to_pow10(a: i64) -> i64 {
217224
let res = (a as f64) / f64::consts::LOG2_10;
218225
res as i64
219226
}
220227

221228
#[cfg(target_has_reliable_f16)]
222-
impl RawFloat for f16 {
229+
impl Float for f16 {
223230
type Int = u16;
224231

225232
const INFINITY: Self = Self::INFINITY;
@@ -236,33 +243,39 @@ impl RawFloat for f16 {
236243
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5;
237244
const SMALLEST_POWER_OF_TEN: i32 = -27;
238245

239-
#[inline]
240-
fn from_u64(v: u64) -> Self {
241-
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
242-
v as _
246+
fn to_bits(self) -> Self::Int {
247+
self.to_bits()
248+
}
249+
250+
fn classify(self) -> FpCategory {
251+
self.classify()
243252
}
253+
}
244254

255+
#[cfg(target_has_reliable_f16)]
256+
impl FloatExt for f16 {
245257
#[inline]
246258
fn from_u64_bits(v: u64) -> Self {
247259
Self::from_bits((v & 0xFFFF) as u16)
248260
}
261+
}
249262

263+
#[cfg(target_has_reliable_f16)]
264+
impl Lemire for f16 {
250265
fn pow10_fast_path(exponent: usize) -> Self {
251266
#[allow(clippy::use_self)]
252267
const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.];
253268
TABLE[exponent & 7]
254269
}
255270

256-
fn to_bits(self) -> Self::Int {
257-
self.to_bits()
258-
}
259-
260-
fn classify(self) -> FpCategory {
261-
self.classify()
271+
#[inline]
272+
fn from_u64(v: u64) -> Self {
273+
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
274+
v as _
262275
}
263276
}
264277

265-
impl RawFloat for f32 {
278+
impl Float for f32 {
266279
type Int = u32;
267280

268281
const INFINITY: Self = f32::INFINITY;
@@ -279,34 +292,38 @@ impl RawFloat for f32 {
279292
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10;
280293
const SMALLEST_POWER_OF_TEN: i32 = -65;
281294

282-
#[inline]
283-
fn from_u64(v: u64) -> Self {
284-
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
285-
v as _
295+
fn to_bits(self) -> Self::Int {
296+
self.to_bits()
286297
}
287298

299+
fn classify(self) -> FpCategory {
300+
self.classify()
301+
}
302+
}
303+
304+
impl FloatExt for f32 {
288305
#[inline]
289306
fn from_u64_bits(v: u64) -> Self {
290307
f32::from_bits((v & 0xFFFFFFFF) as u32)
291308
}
309+
}
292310

311+
impl Lemire for f32 {
293312
fn pow10_fast_path(exponent: usize) -> Self {
294313
#[allow(clippy::use_self)]
295314
const TABLE: [f32; 16] =
296315
[1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.];
297316
TABLE[exponent & 15]
298317
}
299318

300-
fn to_bits(self) -> Self::Int {
301-
self.to_bits()
302-
}
303-
304-
fn classify(self) -> FpCategory {
305-
self.classify()
319+
#[inline]
320+
fn from_u64(v: u64) -> Self {
321+
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
322+
v as _
306323
}
307324
}
308325

309-
impl RawFloat for f64 {
326+
impl Float for f64 {
310327
type Int = u64;
311328

312329
const INFINITY: Self = Self::INFINITY;
@@ -323,17 +340,23 @@ impl RawFloat for f64 {
323340
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23;
324341
const SMALLEST_POWER_OF_TEN: i32 = -342;
325342

326-
#[inline]
327-
fn from_u64(v: u64) -> Self {
328-
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
329-
v as _
343+
fn to_bits(self) -> Self::Int {
344+
self.to_bits()
330345
}
331346

347+
fn classify(self) -> FpCategory {
348+
self.classify()
349+
}
350+
}
351+
352+
impl FloatExt for f64 {
332353
#[inline]
333354
fn from_u64_bits(v: u64) -> Self {
334355
f64::from_bits(v)
335356
}
357+
}
336358

359+
impl Lemire for f64 {
337360
fn pow10_fast_path(exponent: usize) -> Self {
338361
const TABLE: [f64; 32] = [
339362
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
@@ -342,11 +365,9 @@ impl RawFloat for f64 {
342365
TABLE[exponent & 31]
343366
}
344367

345-
fn to_bits(self) -> Self::Int {
346-
self.to_bits()
347-
}
348-
349-
fn classify(self) -> FpCategory {
350-
self.classify()
368+
#[inline]
369+
fn from_u64(v: u64) -> Self {
370+
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
371+
v as _
351372
}
352373
}

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

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

77
use crate::num::imp::dec2flt;
@@ -24,7 +24,7 @@ use crate::num::imp::dec2flt;
2424
/// at a Gigabyte per Second" in section 5, "Fast Algorithm", and
2525
/// section 6, "Exact Numbers And Ties", available online:
2626
/// <https://arxiv.org/abs/2101.11408.pdf>.
27-
pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
27+
pub fn compute_float<F: Float>(q: i64, mut w: u64) -> BiasedFp {
2828
let fp_zero = BiasedFp::zero_pow2(0);
2929
let fp_inf = BiasedFp::zero_pow2(F::INFINITE_POWER);
3030
let fp_error = BiasedFp::zero_pow2(-1);

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

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

9090
use common::BiasedFp;
91-
use float::RawFloat;
91+
use float::{FloatExt, Lemire};
9292
use lemire::compute_float;
9393
use parse::{parse_inf_nan, parse_number};
9494
use slow::parse_long_mantissa;
@@ -120,15 +120,15 @@ pub fn pfe_invalid() -> ParseFloatError {
120120
}
121121

122122
/// Converts a `BiasedFp` to the closest machine float type.
123-
fn biased_fp_to_float<F: RawFloat>(x: BiasedFp) -> F {
123+
fn biased_fp_to_float<F: FloatExt>(x: BiasedFp) -> F {
124124
let mut word = x.m;
125125
word |= (x.p_biased as u64) << F::SIG_BITS;
126126
F::from_u64_bits(word)
127127
}
128128

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

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

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

77
use crate::num::imp::dec2flt;
88

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

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

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

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

77
use crate::num::imp::dec2flt;
88

@@ -25,7 +25,7 @@ use crate::num::imp::dec2flt;
2525
///
2626
/// The algorithms described here are based on "Processing Long Numbers Quickly",
2727
/// available here: <https://arxiv.org/pdf/2101.11408.pdf#section.11>.
28-
pub(crate) fn parse_long_mantissa<F: RawFloat>(s: &[u8]) -> BiasedFp {
28+
pub(crate) fn parse_long_mantissa<F: Float>(s: &[u8]) -> BiasedFp {
2929
const MAX_SHIFT: usize = 60;
3030
const NUM_POWERS: usize = 19;
3131
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::dec2flt::float::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
}

0 commit comments

Comments
 (0)