Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions library/core/src/num/imp/dec2flt/decimal.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Representation of a float as the significant digits and exponent.

use dec2flt::float::RawFloat;
use dec2flt::Lemire;
use dec2flt::fpu::set_precision;

use crate::num::imp::dec2flt;
Expand Down Expand Up @@ -36,7 +36,7 @@ pub struct Decimal {
impl Decimal {
/// Detect if the float can be accurately reconstructed from native floats.
#[inline]
fn can_use_fast_path<F: RawFloat>(&self) -> bool {
fn can_use_fast_path<F: Lemire>(&self) -> bool {
F::MIN_EXPONENT_FAST_PATH <= self.exponent
&& self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH
&& self.mantissa <= F::MAX_MANTISSA_FAST_PATH
Expand All @@ -53,7 +53,7 @@ impl Decimal {
///
/// 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> {
pub fn try_fast_path<F: Lemire>(&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
Expand Down
5 changes: 2 additions & 3 deletions library/core/src/num/imp/dec2flt/lemire.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//! Implementation of the Eisel-Lemire algorithm.

use dec2flt::common::BiasedFp;
use dec2flt::float::RawFloat;
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.
///
Expand All @@ -24,7 +23,7 @@ use crate::num::imp::dec2flt;
/// at a Gigabyte per Second" in section 5, "Fast Algorithm", and
/// section 6, "Exact Numbers And Ties", available online:
/// <https://arxiv.org/abs/2101.11408.pdf>.
pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
pub fn compute_float<F: Float>(q: i64, mut w: u64) -> BiasedFp {
let fp_zero = BiasedFp::zero_pow2(0);
let fp_inf = BiasedFp::zero_pow2(F::INFINITE_POWER);
let fp_error = BiasedFp::zero_pow2(-1);
Expand Down
94 changes: 87 additions & 7 deletions library/core/src/num/imp/dec2flt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,104 @@
)]

use common::BiasedFp;
use float::RawFloat;
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 {
Expand All @@ -120,15 +200,15 @@ pub fn pfe_invalid() -> ParseFloatError {
}

/// Converts a `BiasedFp` to the closest machine float type.
fn biased_fp_to_float<F: RawFloat>(x: BiasedFp) -> F {
fn biased_fp_to_float<F: FloatExt>(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.
#[inline(always)] // Will be inlined into a function with `#[inline(never)]`, see above
pub fn dec2flt<F: RawFloat>(s: &str) -> Result<F, ParseFloatError> {
pub fn dec2flt<F: Lemire>(s: &str) -> Result<F, ParseFloatError> {
let mut s = s.as_bytes();
let Some(&c) = s.first() else { return Err(pfe_empty()) };
let negative = c == b'-';
Expand Down
5 changes: 2 additions & 3 deletions library/core/src/num/imp/dec2flt/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

use dec2flt::common::{ByteSlice, is_8digits};
use dec2flt::decimal::Decimal;
use dec2flt::float::RawFloat;

use crate::num::imp::dec2flt;
use crate::num::imp::{Float, dec2flt};

const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000;

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

/// Try to parse a special, non-finite float.
pub(crate) fn parse_inf_nan<F: RawFloat>(s: &[u8], negative: bool) -> Option<F> {
pub(crate) fn parse_inf_nan<F: Float>(s: &[u8], negative: bool) -> Option<F> {
// Since a valid string has at most the length 8, we can load
// all relevant characters into a u64 and work from there.
// This also generates much better code.
Expand Down
5 changes: 2 additions & 3 deletions library/core/src/num/imp/dec2flt/slow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

use dec2flt::common::BiasedFp;
use dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq};
use dec2flt::float::RawFloat;

use crate::num::imp::dec2flt;
use crate::num::imp::{Float, dec2flt};

/// Parse the significant digits and biased, binary exponent of a float.
///
Expand All @@ -25,7 +24,7 @@ use crate::num::imp::dec2flt;
///
/// The algorithms described here are based on "Processing Long Numbers Quickly",
/// available here: <https://arxiv.org/pdf/2101.11408.pdf#section.11>.
pub(crate) fn parse_long_mantissa<F: RawFloat>(s: &[u8]) -> BiasedFp {
pub(crate) fn parse_long_mantissa<F: Float>(s: &[u8]) -> BiasedFp {
const MAX_SHIFT: usize = 60;
const NUM_POWERS: usize = 19;
const POWERS: [u8; 19] =
Expand Down
4 changes: 2 additions & 2 deletions library/core/src/num/imp/flt2dec/decoder.rs
Original file line number Diff line number Diff line change
@@ -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::RawFloat;
use crate::num::imp::FloatExt;

/// Decoded unsigned finite value, such that:
///
Expand Down Expand Up @@ -40,7 +40,7 @@ pub enum FullDecoded {
}

/// A floating point type which can be `decode`d.
pub trait DecodableFloat: RawFloat + Copy {
pub trait DecodableFloat: FloatExt + Copy {
/// The minimum positive normalized value.
fn min_pos_norm_value() -> Self;
}
Expand Down
3 changes: 3 additions & 0 deletions library/core/src/num/imp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ pub(crate) mod int_log10;
pub(crate) mod int_sqrt;
pub(crate) mod libm;
pub(crate) mod overflow_panic;
mod traits;

pub use traits::{Float, FloatExt, Int};
Loading
Loading