Rollup merge of #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 https://github.com/rust-lang/rust/pull/151900
This commit is contained in:
Jonathan Brouwer
2026-03-19 13:42:32 +01:00
committed by GitHub
11 changed files with 204 additions and 177 deletions
+3 -3
View File
@@ -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;
@@ -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
@@ -53,7 +53,7 @@ fn can_use_fast_path<F: RawFloat>(&self) -> bool {
///
/// 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
+2 -3
View File
@@ -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.
///
@@ -24,7 +23,7 @@
/// 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);
+87 -7
View File
@@ -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 {
@@ -120,7 +200,7 @@ 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)
@@ -128,7 +208,7 @@ fn biased_fp_to_float<F: RawFloat>(x: BiasedFp) -> F {
/// 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'-';
+2 -3
View File
@@ -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;
@@ -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.
+2 -3
View File
@@ -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.
///
@@ -25,7 +24,7 @@
///
/// 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] =
+2 -2
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::RawFloat;
use crate::num::imp::FloatExt;
/// Decoded unsigned finite value, such that:
///
@@ -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;
}
+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 {
@@ -12,11 +16,11 @@ pub trait CastInto<T: Copy>: Copy {
}
/// Collection of traits that allow us to be generic over integer size.
pub trait Integer:
pub trait Int:
Sized
+ Clone
+ Copy
+ Debug
+ fmt::Debug
+ ops::Shr<u32, Output = Self>
+ ops::Shl<u32, Output = Self>
+ ops::BitAnd<Output = Self>
@@ -37,7 +41,7 @@ fn cast(self) -> i16 {
}
}
impl Integer for $ty {
impl Int for $ty {
const ZERO: Self = 0;
const ONE: Self = 1;
}
@@ -48,27 +52,22 @@ impl Integer for $ty {
int!(u16, u32, u64);
/// A helper trait to avoid duplicating basically all the conversion code for IEEE floats.
///
/// See the parent module's doc comment for why this is necessary.
///
/// Should **never ever** be implemented for other types or be used outside the `dec2flt` module.
#[doc(hidden)]
pub trait RawFloat:
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: Integer + Into<u64>;
type Int: Int + Into<u64>;
/* general constants */
@@ -128,8 +127,6 @@ pub trait RawFloat:
const MIN_EXPONENT_ROUND_TO_EVEN: i32;
const MAX_EXPONENT_ROUND_TO_EVEN: i32;
/* limits related to Fast pathing */
/// Largest decimal exponent for a non-infinite value.
///
/// This is the max exponent in binary converted to the max exponent in decimal. Allows fast
@@ -151,41 +148,19 @@ pub trait RawFloat:
/// compile time since intermediates exceed the range of an `f64`.
const SMALLEST_POWER_OF_TEN: i32;
/// 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;
/// 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;
/// Performs a raw transmutation from an integer.
fn from_u64_bits(v: u64) -> Self;
/// Gets a small power-of-ten for fast-path multiplication.
fn pow10_fast_path(exponent: usize) -> Self;
/// Returns the category that this number falls into.
fn classify(self) -> FpCategory;
/// Transmute to the integer representation
fn to_bits(self) -> Self::Int;
}
/// Items that ideally would be on `Float`, but don't apply to all float types because they
/// rely on the mantissa fitting into a `u64` (which isn't true for `f128`).
#[doc(hidden)]
pub trait FloatExt: Float {
/// Performs a raw transmutation from an integer.
fn from_u64_bits(v: u64) -> Self;
/// Returns the mantissa, exponent and sign as integers.
///
@@ -219,7 +194,7 @@ const fn pow2_to_pow10(a: i64) -> i64 {
}
#[cfg(target_has_reliable_f16)]
impl RawFloat for f16 {
impl Float for f16 {
type Int = u16;
const INFINITY: Self = Self::INFINITY;
@@ -236,23 +211,6 @@ impl RawFloat for f16 {
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5;
const SMALLEST_POWER_OF_TEN: i32 = -27;
#[inline]
fn from_u64(v: u64) -> Self {
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
v as _
}
#[inline]
fn from_u64_bits(v: u64) -> Self {
Self::from_bits((v & 0xFFFF) as u16)
}
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]
}
fn to_bits(self) -> Self::Int {
self.to_bits()
}
@@ -262,7 +220,15 @@ fn classify(self) -> FpCategory {
}
}
impl RawFloat for f32 {
#[cfg(target_has_reliable_f16)]
impl FloatExt for f16 {
#[inline]
fn from_u64_bits(v: u64) -> Self {
Self::from_bits((v & 0xFFFF) as u16)
}
}
impl Float for f32 {
type Int = u32;
const INFINITY: Self = f32::INFINITY;
@@ -279,24 +245,6 @@ impl RawFloat for f32 {
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10;
const SMALLEST_POWER_OF_TEN: i32 = -65;
#[inline]
fn from_u64(v: u64) -> Self {
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
v as _
}
#[inline]
fn from_u64_bits(v: u64) -> Self {
f32::from_bits((v & 0xFFFFFFFF) as u32)
}
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]
}
fn to_bits(self) -> Self::Int {
self.to_bits()
}
@@ -306,7 +254,14 @@ fn classify(self) -> FpCategory {
}
}
impl RawFloat for f64 {
impl FloatExt for f32 {
#[inline]
fn from_u64_bits(v: u64) -> Self {
f32::from_bits((v & 0xFFFFFFFF) as u32)
}
}
impl Float for f64 {
type Int = u64;
const INFINITY: Self = Self::INFINITY;
@@ -323,25 +278,6 @@ impl RawFloat for f64 {
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23;
const SMALLEST_POWER_OF_TEN: i32 = -342;
#[inline]
fn from_u64(v: u64) -> Self {
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
v as _
}
#[inline]
fn from_u64_bits(v: u64) -> Self {
f64::from_bits(v)
}
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]
}
fn to_bits(self) -> Self::Int {
self.to_bits()
}
@@ -350,3 +286,10 @@ fn classify(self) -> FpCategory {
self.classify()
}
}
impl FloatExt for f64 {
#[inline]
fn from_u64_bits(v: u64) -> Self {
f64::from_bits(v)
}
}
+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)]
+50 -46
View File
@@ -1,4 +1,5 @@
use core::num::imp::dec2flt::float::RawFloat;
use core::num::imp::dec2flt::Lemire;
use core::num::imp::{Float, FloatExt};
use crate::num::{ldexp_f32, ldexp_f64};
@@ -56,57 +57,60 @@ fn test_f64_integer_decode() {
#[test]
#[cfg(target_has_reliable_f16)]
fn test_f16_consts() {
assert_eq!(<f16 as RawFloat>::INFINITY, f16::INFINITY);
assert_eq!(<f16 as RawFloat>::NEG_INFINITY, -f16::INFINITY);
assert_eq!(<f16 as RawFloat>::NAN.to_bits(), f16::NAN.to_bits());
assert_eq!(<f16 as RawFloat>::NEG_NAN.to_bits(), (-f16::NAN).to_bits());
assert_eq!(<f16 as RawFloat>::SIG_BITS, 10);
assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 5);
assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_FAST_PATH, -4);
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_FAST_PATH, 4);
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 7);
assert_eq!(<f16 as RawFloat>::EXP_MIN, -14);
assert_eq!(<f16 as RawFloat>::EXP_SAT, 0x1f);
assert_eq!(<f16 as RawFloat>::SMALLEST_POWER_OF_TEN, -27);
assert_eq!(<f16 as RawFloat>::LARGEST_POWER_OF_TEN, 4);
assert_eq!(<f16 as RawFloat>::MAX_MANTISSA_FAST_PATH, 2048);
assert_eq!(<f16 as Float>::INFINITY, f16::INFINITY);
assert_eq!(<f16 as Float>::NEG_INFINITY, -f16::INFINITY);
assert_eq!(<f16 as Float>::NAN.to_bits(), f16::NAN.to_bits());
assert_eq!(<f16 as Float>::NEG_NAN.to_bits(), (-f16::NAN).to_bits());
assert_eq!(<f16 as Float>::SIG_BITS, 10);
assert_eq!(<f16 as Float>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
assert_eq!(<f16 as Float>::MAX_EXPONENT_ROUND_TO_EVEN, 5);
assert_eq!(<f16 as Float>::EXP_MIN, -14);
assert_eq!(<f16 as Float>::EXP_SAT, 0x1f);
assert_eq!(<f16 as Float>::SMALLEST_POWER_OF_TEN, -27);
assert_eq!(<f16 as Float>::LARGEST_POWER_OF_TEN, 4);
assert_eq!(<f16 as Lemire>::MIN_EXPONENT_FAST_PATH, -4);
assert_eq!(<f16 as Lemire>::MAX_EXPONENT_FAST_PATH, 4);
assert_eq!(<f16 as Lemire>::MAX_EXPONENT_DISGUISED_FAST_PATH, 7);
assert_eq!(<f16 as Lemire>::MAX_MANTISSA_FAST_PATH, 2048);
}
#[test]
fn test_f32_consts() {
assert_eq!(<f32 as RawFloat>::INFINITY, f32::INFINITY);
assert_eq!(<f32 as RawFloat>::NEG_INFINITY, -f32::INFINITY);
assert_eq!(<f32 as RawFloat>::NAN.to_bits(), f32::NAN.to_bits());
assert_eq!(<f32 as RawFloat>::NEG_NAN.to_bits(), (-f32::NAN).to_bits());
assert_eq!(<f32 as RawFloat>::SIG_BITS, 23);
assert_eq!(<f32 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -17);
assert_eq!(<f32 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 10);
assert_eq!(<f32 as RawFloat>::MIN_EXPONENT_FAST_PATH, -10);
assert_eq!(<f32 as RawFloat>::MAX_EXPONENT_FAST_PATH, 10);
assert_eq!(<f32 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 17);
assert_eq!(<f32 as RawFloat>::EXP_MIN, -126);
assert_eq!(<f32 as RawFloat>::EXP_SAT, 0xff);
assert_eq!(<f32 as RawFloat>::SMALLEST_POWER_OF_TEN, -65);
assert_eq!(<f32 as RawFloat>::LARGEST_POWER_OF_TEN, 38);
assert_eq!(<f32 as RawFloat>::MAX_MANTISSA_FAST_PATH, 16777216);
assert_eq!(<f32 as Float>::INFINITY, f32::INFINITY);
assert_eq!(<f32 as Float>::NEG_INFINITY, -f32::INFINITY);
assert_eq!(<f32 as Float>::NAN.to_bits(), f32::NAN.to_bits());
assert_eq!(<f32 as Float>::NEG_NAN.to_bits(), (-f32::NAN).to_bits());
assert_eq!(<f32 as Float>::SIG_BITS, 23);
assert_eq!(<f32 as Float>::MIN_EXPONENT_ROUND_TO_EVEN, -17);
assert_eq!(<f32 as Float>::MAX_EXPONENT_ROUND_TO_EVEN, 10);
assert_eq!(<f32 as Float>::EXP_MIN, -126);
assert_eq!(<f32 as Float>::EXP_SAT, 0xff);
assert_eq!(<f32 as Float>::SMALLEST_POWER_OF_TEN, -65);
assert_eq!(<f32 as Float>::LARGEST_POWER_OF_TEN, 38);
assert_eq!(<f32 as Lemire>::MIN_EXPONENT_FAST_PATH, -10);
assert_eq!(<f32 as Lemire>::MAX_EXPONENT_FAST_PATH, 10);
assert_eq!(<f32 as Lemire>::MAX_EXPONENT_DISGUISED_FAST_PATH, 17);
assert_eq!(<f32 as Lemire>::MAX_MANTISSA_FAST_PATH, 16777216);
}
#[test]
fn test_f64_consts() {
assert_eq!(<f64 as RawFloat>::INFINITY, f64::INFINITY);
assert_eq!(<f64 as RawFloat>::NEG_INFINITY, -f64::INFINITY);
assert_eq!(<f64 as RawFloat>::NAN.to_bits(), f64::NAN.to_bits());
assert_eq!(<f64 as RawFloat>::NEG_NAN.to_bits(), (-f64::NAN).to_bits());
assert_eq!(<f64 as RawFloat>::SIG_BITS, 52);
assert_eq!(<f64 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -4);
assert_eq!(<f64 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 23);
assert_eq!(<f64 as RawFloat>::MIN_EXPONENT_FAST_PATH, -22);
assert_eq!(<f64 as RawFloat>::MAX_EXPONENT_FAST_PATH, 22);
assert_eq!(<f64 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 37);
assert_eq!(<f64 as RawFloat>::EXP_MIN, -1022);
assert_eq!(<f64 as RawFloat>::EXP_SAT, 0x7ff);
assert_eq!(<f64 as RawFloat>::SMALLEST_POWER_OF_TEN, -342);
assert_eq!(<f64 as RawFloat>::LARGEST_POWER_OF_TEN, 308);
assert_eq!(<f64 as RawFloat>::MAX_MANTISSA_FAST_PATH, 9007199254740992);
assert_eq!(<f64 as Float>::INFINITY, f64::INFINITY);
assert_eq!(<f64 as Float>::NEG_INFINITY, -f64::INFINITY);
assert_eq!(<f64 as Float>::NAN.to_bits(), f64::NAN.to_bits());
assert_eq!(<f64 as Float>::NEG_NAN.to_bits(), (-f64::NAN).to_bits());
assert_eq!(<f64 as Float>::SIG_BITS, 52);
assert_eq!(<f64 as Float>::MIN_EXPONENT_ROUND_TO_EVEN, -4);
assert_eq!(<f64 as Float>::MAX_EXPONENT_ROUND_TO_EVEN, 23);
assert_eq!(<f64 as Float>::EXP_MIN, -1022);
assert_eq!(<f64 as Float>::EXP_SAT, 0x7ff);
assert_eq!(<f64 as Float>::SMALLEST_POWER_OF_TEN, -342);
assert_eq!(<f64 as Float>::LARGEST_POWER_OF_TEN, 308);
assert_eq!(<f64 as Lemire>::MIN_EXPONENT_FAST_PATH, -22);
assert_eq!(<f64 as Lemire>::MAX_EXPONENT_FAST_PATH, 22);
assert_eq!(<f64 as Lemire>::MAX_EXPONENT_DISGUISED_FAST_PATH, 37);
assert_eq!(<f64 as Lemire>::MAX_MANTISSA_FAST_PATH, 9007199254740992);
}
@@ -1,6 +1,5 @@
use core::num::imp::dec2flt;
use core::num::imp::{Float, dec2flt};
use dec2flt::float::RawFloat;
use dec2flt::lemire::compute_float;
#[cfg(target_has_reliable_f16)]