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.
This commit is contained in:
Trevor Gross
2026-01-31 03:13:08 -06:00
parent b07c0439a4
commit bec94a33d5
11 changed files with 113 additions and 109 deletions
+3 -2
View File
@@ -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,
+1 -2
View File
@@ -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.
///
+85 -5
View File
@@ -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 {
+1 -2
View File
@@ -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;
+1 -2
View File
@@ -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.
///
+1 -1
View File
@@ -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:
///
+3
View File
@@ -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};
@@ -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<T: Copy>: Copy {
@@ -16,7 +20,7 @@ pub trait Int:
Sized
+ Clone
+ Copy
+ Debug
+ fmt::Debug
+ ops::Shr<u32, Output = Self>
+ ops::Shl<u32, Output = Self>
+ ops::BitAnd<Output = Self>
@@ -51,17 +55,16 @@ impl Int for $ty {
#[doc(hidden)]
pub trait Float:
Sized
+ Div<Output = Self>
+ Neg<Output = Self>
+ Mul<Output = Self>
+ Add<Output = Self>
+ LowerExp
+ ops::Div<Output = Self>
+ ops::Neg<Output = Self>
+ ops::Mul<Output = Self>
+ ops::Add<Output = Self>
+ fmt::Debug
+ PartialEq
+ PartialOrd
+ Default
+ Clone
+ Copy
+ Debug
{
/// The unsigned integer with the same size as the float
type Int: Int + Into<u64>;
@@ -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 _
}
}
+1
View File
@@ -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)]
+2 -1
View File
@@ -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};
@@ -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)]