diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs index 7bf6f4b73d8ce..08ee0ac06a41a 100644 --- a/library/core/src/num/dec2flt/float.rs +++ b/library/core/src/num/dec2flt/float.rs @@ -2,10 +2,50 @@ use crate::fmt::{Debug, LowerExp}; use crate::num::FpCategory; -use crate::ops::{Add, Div, Mul, Neg}; +use crate::ops::{self, Add, Div, Mul, Neg}; use core::f64; +pub trait CastInto: Copy { + fn cast(self) -> T; +} + +pub trait Integer: + Sized + + Clone + + Copy + + Debug + + ops::Shr + + ops::Shl + + ops::BitAnd + + ops::BitOr + + PartialEq + + CastInto +{ + const ZERO: Self; + const ONE: Self; +} + +macro_rules! int { + ($($ty:ty),+) => { + $( + impl CastInto for $ty { + fn cast(self) -> i16 { + self as i16 + } + } + + + impl Integer for $ty { + const ZERO: Self = 0; + const ONE: Self = 1; + } + )+ + } +} + +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. @@ -26,6 +66,9 @@ pub trait RawFloat: + Copy + Debug { + /// The unsigned integer with the same size as the float + type Int: Integer + Into; + /* general constants */ const INFINITY: Self; @@ -39,6 +82,9 @@ pub trait RawFloat: /// Mantissa digits including the hidden bit (provided by core) const MANTISSA_BITS: u32; + const EXPONENT_MASK: Self::Int; + const MANTISSA_MASK: Self::Int; + /// The number of bits in the significand, *excluding* the hidden bit. const MANTISSA_EXPLICIT_BITS: u32 = Self::MANTISSA_BITS - 1; @@ -91,13 +137,14 @@ pub trait RawFloat: /// This is the max exponent in binary converted to the max exponent in decimal. Allows fast /// pathing anything larger than `10^LARGEST_POWER_OF_TEN`, which will round to infinity. // const LARGEST_POWER_OF_TEN: i32; - const LARGEST_POWER_OF_TEN: i32 = (Self::EXPONENT_BIAS as f64 / f64::consts::LOG2_10) as i32; + const LARGEST_POWER_OF_TEN: i32 = + ((Self::EXPONENT_BIAS as f64 + 1.0) / f64::consts::LOG2_10) as i32; /// Smallest decimal exponent for a non-zero value. This allows for fast pathing anything - /// smaller than `10^SMALLEST_POWER_OF_TEN`. - const SMALLEST_POWER_OF_TEN: i32; - // const SMALLEST_POWER_OF_TEN: i32 = - // -(((Self::EXPONENT_BIAS + Self::MANTISSA_BITS) as f64) / f64::consts::LOG2_10) as i32 - 2; + // / smaller than `10^SMALLEST_POWER_OF_TEN`. + // const SMALLEST_POWER_OF_TEN: i32; + const SMALLEST_POWER_OF_TEN: i32 = + -(((Self::EXPONENT_BIAS + Self::MANTISSA_BITS + 64) as f64) / f64::consts::LOG2_10) as i32; /// Maximum exponent that can be represented for a disguised-fast path case. /// This is `MAX_EXPONENT_FAST_PATH + ⌊(MANTISSA_EXPLICIT_BITS+1)/log2(10)⌋` @@ -122,65 +169,74 @@ pub trait RawFloat: /// Returns the category that this number falls into. fn classify(self) -> FpCategory; + /// Transmute to the integer representation + fn to_bits(self) -> Self::Int; + /// Returns the mantissa, exponent and sign as integers. - fn integer_decode(self) -> (u64, i16, i8); + fn integer_decode(self) -> (u64, i16, i8) { + let bits = self.to_bits(); + let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 }; + let mut exponent: i16 = + ((bits & Self::EXPONENT_MASK) >> Self::MANTISSA_EXPLICIT_BITS).cast(); + let mantissa = if exponent == 0 { + (bits & Self::MANTISSA_MASK) << 1 + } else { + (bits & Self::MANTISSA_MASK) | (Self::Int::ONE << Self::MANTISSA_EXPLICIT_BITS) + }; + // Exponent bias + mantissa shift + exponent -= (Self::EXPONENT_BIAS + Self::MANTISSA_EXPLICIT_BITS) as i16; + (mantissa.into(), exponent, sign) + } } -// #[cfg(not(bootstrap))] -// impl RawFloat for f16 { -// const INFINITY: Self = Self::INFINITY; -// const NEG_INFINITY: Self = Self::NEG_INFINITY; -// const NAN: Self = Self::NAN; -// const NEG_NAN: Self = -Self::NAN; - -// const BITS: u32 = 16; -// const MANTISSA_DIGITS: u32 = Self::MANTISSA_DIGITS; - -// const MIN_EXPONENT_FAST_PATH: i64 = -4; // assuming FLT_EVAL_METHOD = 0 -// const MAX_EXPONENT_FAST_PATH: i64 = 4; - -// const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17; -// const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10; -// const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = 17; -// const SMALLEST_POWER_OF_TEN: i32 = -65; -// const LARGEST_POWER_OF_TEN: i32 = Self::MAX_10_EXP; - -// #[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: [f32; 16] = -// [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.]; -// TABLE[exponent & 15] -// } - -// /// Returns the mantissa, exponent and sign as integers. -// fn integer_decode(self) -> (u64, i16, i8) { -// let bits = self.to_bits(); -// let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 }; -// let mut exponent: i16 = ((bits >> 23) & 0xff) as i16; -// let mantissa = -// if exponent == 0 { (bits & 0x7fffff) << 1 } else { (bits & 0x7fffff) | 0x800000 }; -// // Exponent bias + mantissa shift -// exponent -= 127 + 23; -// (mantissa as u64, exponent, sign) -// } - -// fn classify(self) -> FpCategory { -// self.classify() -// } -// } +#[cfg(not(bootstrap))] +impl RawFloat for f16 { + type Int = u16; + + const INFINITY: Self = Self::INFINITY; + const NEG_INFINITY: Self = Self::NEG_INFINITY; + const NAN: Self = Self::NAN; + const NEG_NAN: Self = -Self::NAN; + + const BITS: u32 = 16; + const MANTISSA_BITS: u32 = Self::MANTISSA_DIGITS; + const EXPONENT_MASK: Self::Int = Self::EXP_MASK; + const MANTISSA_MASK: Self::Int = Self::MAN_MASK; + + const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17; + const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10; + // const SMALLEST_POWER_OF_TEN: i32 = -27; + // const LARGEST_POWER_OF_TEN: i32 = Self::MAX_10_EXP; + + #[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 & 0xFF) 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 & 15] + } + + fn to_bits(self) -> Self::Int { + self.to_bits() + } + + fn classify(self) -> FpCategory { + todo!() + } +} impl RawFloat for f32 { + type Int = u32; + const INFINITY: Self = f32::INFINITY; const NEG_INFINITY: Self = f32::NEG_INFINITY; const NAN: Self = f32::NAN; @@ -188,6 +244,8 @@ impl RawFloat for f32 { const BITS: u32 = 32; const MANTISSA_BITS: u32 = Self::MANTISSA_DIGITS; + const EXPONENT_MASK: Self::Int = Self::EXP_MASK; + const MANTISSA_MASK: Self::Int = Self::MAN_MASK; // const MANTISSA_EXPLICIT_BITS: u32 = 23; const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17; @@ -198,7 +256,7 @@ impl RawFloat for f32 { // const MINIMUM_EXPONENT: i32 = -127; // const INFINITE_POWER: i32 = 0xFF; // const SIGN_INDEX: u32 = 31; - const SMALLEST_POWER_OF_TEN: i32 = -65; + // const SMALLEST_POWER_OF_TEN: i32 = -65; // const LARGEST_POWER_OF_TEN: i32 = 38; #[inline] @@ -219,16 +277,8 @@ impl RawFloat for f32 { TABLE[exponent & 15] } - /// Returns the mantissa, exponent and sign as integers. - fn integer_decode(self) -> (u64, i16, i8) { - let bits = self.to_bits(); - let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 }; - let mut exponent: i16 = ((bits >> 23) & 0xff) as i16; - let mantissa = - if exponent == 0 { (bits & 0x7fffff) << 1 } else { (bits & 0x7fffff) | 0x800000 }; - // Exponent bias + mantissa shift - exponent -= 127 + 23; - (mantissa as u64, exponent, sign) + fn to_bits(self) -> Self::Int { + self.to_bits() } fn classify(self) -> FpCategory { @@ -237,6 +287,8 @@ impl RawFloat for f32 { } impl RawFloat for f64 { + type Int = u64; + const INFINITY: Self = Self::INFINITY; const NEG_INFINITY: Self = Self::NEG_INFINITY; const NAN: Self = Self::NAN; @@ -244,6 +296,8 @@ impl RawFloat for f64 { const BITS: u32 = 64; const MANTISSA_BITS: u32 = Self::MANTISSA_DIGITS; + const EXPONENT_MASK: Self::Int = Self::EXP_MASK; + const MANTISSA_MASK: Self::Int = Self::MAN_MASK; // const MANTISSA_EXPLICIT_BITS: u32 = 52; const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -4; @@ -254,7 +308,7 @@ impl RawFloat for f64 { // const MINIMUM_EXPONENT: i32 = -1023; // const INFINITE_POWER: i32 = 0x7FF; // const SIGN_INDEX: u32 = 63; - const SMALLEST_POWER_OF_TEN: i32 = -342; + // const SMALLEST_POWER_OF_TEN: i32 = -342; // const LARGEST_POWER_OF_TEN: i32 = 308; #[inline] @@ -276,19 +330,8 @@ impl RawFloat for f64 { TABLE[exponent & 31] } - /// Returns the mantissa, exponent and sign as integers. - fn integer_decode(self) -> (u64, i16, i8) { - let bits = self.to_bits(); - let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 }; - let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16; - let mantissa = if exponent == 0 { - (bits & 0xfffffffffffff) << 1 - } else { - (bits & 0xfffffffffffff) | 0x10000000000000 - }; - // Exponent bias + mantissa shift - exponent -= 1023 + 52; - (mantissa, exponent, sign) + fn to_bits(self) -> Self::Int { + self.to_bits() } fn classify(self) -> FpCategory { diff --git a/library/core/src/num/dec2flt/lemire.rs b/library/core/src/num/dec2flt/lemire.rs index a193163810adb..98b4038d69869 100644 --- a/library/core/src/num/dec2flt/lemire.rs +++ b/library/core/src/num/dec2flt/lemire.rs @@ -89,7 +89,7 @@ pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { if lo <= 1 && q >= F::MIN_EXPONENT_ROUND_TO_EVEN as i64 && q <= F::MAX_EXPONENT_ROUND_TO_EVEN as i64 - && mantissa & 3 == 1 + && mantissa & 0b11 == 1 && (mantissa << (upperbit + 64 - F::MANTISSA_EXPLICIT_BITS as i32 - 3)) == hi { // Zero the lowest bit, so we don't round up. diff --git a/library/core/src/num/dec2flt/number.rs b/library/core/src/num/dec2flt/number.rs index 3013bb3324ec3..0fcea6c944a1c 100644 --- a/library/core/src/num/dec2flt/number.rs +++ b/library/core/src/num/dec2flt/number.rs @@ -82,10 +82,6 @@ impl Number { F::from_u64(mantissa) * F::pow10_fast_path(F::MAX_EXPONENT_FAST_PATH as _) }; - if self.negative { - value = -value; - } - - Some(value) + if self.negative { Some(-value) } else { Some(value) } } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 2a8ede9383844..d08be1dbfd31c 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -250,9 +250,10 @@ impl f16 { pub(crate) const SIGN_MASK: u16 = 0x8000; /// Exponent mask + #[cfg(not(bootstrap))] pub(crate) const EXP_MASK: u16 = 0x7c00; - /// Mantissa mask + #[cfg(not(bootstrap))] pub(crate) const MAN_MASK: u16 = 0x03ff; /// Minimum representable positive value (min subnormal) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index b9c84a66ed138..69be9e0e4d817 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -491,13 +491,13 @@ impl f32 { pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; /// Sign bit - const SIGN_MASK: u32 = 0x8000_0000; + pub(crate) const SIGN_MASK: u32 = 0x8000_0000; /// Exponent mask - const EXP_MASK: u32 = 0x7f80_0000; + pub(crate) const EXP_MASK: u32 = 0x7f80_0000; /// Mantissa mask - const MAN_MASK: u32 = 0x007f_ffff; + pub(crate) const MAN_MASK: u32 = 0x007f_ffff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u32 = 0x1; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index f8e4555fc44f2..1fe96e3c71403 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -490,13 +490,13 @@ impl f64 { pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; /// Sign bit - const SIGN_MASK: u64 = 0x8000_0000_0000_0000; + pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000; /// Exponent mask - const EXP_MASK: u64 = 0x7ff0_0000_0000_0000; + pub(crate) const EXP_MASK: u64 = 0x7ff0_0000_0000_0000; /// Mantissa mask - const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff; + pub(crate) const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u64 = 0x1; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 83a615fcd8be3..56ab95d1af9a6 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -35,6 +35,7 @@ #![feature(duration_constructors)] #![feature(exact_size_is_empty)] #![feature(extern_types)] +#![feature(f16)] #![feature(freeze)] #![feature(flt2dec)] #![feature(fmt_internals)] diff --git a/library/core/tests/num/dec2flt/mod.rs b/library/core/tests/num/dec2flt/mod.rs index 5c5ddeb3b524e..4c3fffb9e4e47 100644 --- a/library/core/tests/num/dec2flt/mod.rs +++ b/library/core/tests/num/dec2flt/mod.rs @@ -32,6 +32,7 @@ macro_rules! test_literal { // let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)]; // for input in inputs { // println!("{}", input.parse::().unwrap()); +// println!("{}", input.parse::().unwrap()); // // assert_eq!(input.parse(), Ok(x64), "failed f64 {input}"); // // assert_eq!(input.parse(), Ok(x32), "failed f32 {input}"); // // let neg_input = format!("-{input}"); @@ -79,17 +80,22 @@ macro_rules! test_literal { // ); // } -// test_literal2!(1e-20); -// test_literal2!(1e-30); -// test_literal2!(1e-40); -// test_literal2!(1e-50); -// test_literal2!(1e-60); -// test_literal2!(1e-63); -// test_literal2!(1e-64); -// test_literal2!(1e-65); -// test_literal2!(1e-66); -// test_literal2!(1e-70); -// panic!(); +// // test_literal2!(1e-20); +// // test_literal2!(1e-30); +// // test_literal2!(1e-40); +// // test_literal2!(1e-50); +// // test_literal2!(1e-60); +// // test_literal2!(1e-63); +// // test_literal2!(1e-64); +// // test_literal2!(1e-65); +// // test_literal2!(1e-66); +// // test_literal2!(1e-70); +// // test_literal2!(1e-70); +// // test_literal2!(1e-70); +// // test_literal2!(1e-70); +// // test_literal2!(2.225073858507201136057409796709131975934819546351645648023426109724822222021076945516529523908135087914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630666655994938275772572015763062690663332647565300009245888316433037779791869612049497390377829704905051080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884423900265498198385487948292206894721689831099698365846814022854243330660339850886445804001034933970427567186443383770486037861622771738545623065874679014086723327636718749999999999999999999999999999999999999e-308); +// // test_literal2!(1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38); +// // panic!(); // } // #[test] diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs index 83e2707b57e8b..dbcf8dcebc077 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/core/tests/num/flt2dec/mod.rs @@ -77,6 +77,10 @@ macro_rules! try_fixed { }) } +// fn ldexp_f16(a: f16, b: i32) -> f16 { +// ldexp_f64(a as f64, b) as f16 +// } + fn ldexp_f32(a: f32, b: i32) -> f32 { ldexp_f64(a as f64, b) as f32 } @@ -178,6 +182,12 @@ trait TestableFloat: DecodableFloat + fmt::Display { fn ldexpi(f: i64, exp: isize) -> Self; } +// impl TestableFloat for f16 { +// fn ldexpi(f: i64, exp: isize) -> Self { +// f as Self * (exp as Self).exp2() +// } +// } + impl TestableFloat for f32 { fn ldexpi(f: i64, exp: isize) -> Self { f as Self * (exp as Self).exp2() @@ -228,6 +238,99 @@ macro_rules! check_exact_one { // [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion // ftp://ftp.ee.lbl.gov/testbase-report.ps.Z +// #[cfg(not(bootstrap))] +// pub fn f16_shortest_sanity_test(mut f: F) +// where +// F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +// { +// // 0.0999999940395355224609375 +// // 0.100000001490116119384765625 +// // 0.10000000894069671630859375 +// check_shortest!(f(0.1f16) => b"1", 0); + +// // 0.333333313465118408203125 +// // 0.3333333432674407958984375 (1/3 in the default rounding) +// // 0.33333337306976318359375 +// check_shortest!(f(1.0f16/3.0) => b"33333334", 0); + +// // 10^1 * 0.31415917873382568359375 +// // 10^1 * 0.31415920257568359375 +// // 10^1 * 0.31415922641754150390625 +// check_shortest!(f(3.141592f16) => b"3141592", 1); + +// // 10^18 * 0.31415916243714048 +// // 10^18 * 0.314159196796878848 +// // 10^18 * 0.314159231156617216 +// check_shortest!(f(3.141592e17f16) => b"3141592", 18); + +// // regression test for decoders +// // 10^8 * 0.3355443 +// // 10^8 * 0.33554432 +// // 10^8 * 0.33554436 +// check_shortest!(f(ldexp_f16(1.0, 25)) => b"33554432", 8); + +// // 10^39 * 0.340282326356119256160033759537265639424 +// // 10^39 * 0.34028234663852885981170418348451692544 +// // 10^39 * 0.340282366920938463463374607431768211456 +// check_shortest!(f(f16::MAX) => b"34028235", 39); + +// // 10^-37 * 0.1175494210692441075487029444849287348827... +// // 10^-37 * 0.1175494350822287507968736537222245677818... +// // 10^-37 * 0.1175494490952133940450443629595204006810... +// check_shortest!(f(f16::MIN_POSITIVE) => b"11754944", -37); + +// // 10^-44 * 0 +// // 10^-44 * 0.1401298464324817070923729583289916131280... +// // 10^-44 * 0.2802596928649634141847459166579832262560... +// let minf16 = ldexp_f16(1.0, -149); +// check_shortest!(f(minf16) => b"1", -44); +// } + +// #[cfg(not(bootstrap))] +// pub fn f16_exact_sanity_test(mut f: F) +// where +// F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +// { +// let minf16 = ldexp_f16(1.0, -149); + +// check_exact!(f(0.1f16) => b"100000001490116119384765625 ", 0); +// check_exact!(f(0.5f16) => b"5 ", 0); +// check_exact!(f(1.0f16/3.0) => b"3333333432674407958984375 ", 0); +// check_exact!(f(3.141592f16) => b"31415920257568359375 ", 1); +// check_exact!(f(3.141592e17f16) => b"314159196796878848 ", 18); +// check_exact!(f(f16::MAX) => b"34028234663852885981170418348451692544 ", 39); +// check_exact!(f(f16::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37); +// check_exact!(f(minf16) => b"1401298464324817070923729583289916131280", -44); + +// // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP +// check_exact_one!(f(12676506, -102; f16) => b"2", -23); +// check_exact_one!(f(12676506, -103; f16) => b"12", -23); +// check_exact_one!(f(15445013, 86; f16) => b"119", 34); +// check_exact_one!(f(13734123, -138; f16) => b"3941", -34); +// check_exact_one!(f(12428269, -130; f16) => b"91308", -32); +// check_exact_one!(f(15334037, -146; f16) => b"171900", -36); +// check_exact_one!(f(11518287, -41; f16) => b"5237910", -5); +// check_exact_one!(f(12584953, -145; f16) => b"28216440", -36); +// check_exact_one!(f(15961084, -125; f16) => b"375243281", -30); +// check_exact_one!(f(14915817, -146; f16) => b"1672120916", -36); +// check_exact_one!(f(10845484, -102; f16) => b"21388945814", -23); +// check_exact_one!(f(16431059, -61; f16) => b"712583594561", -11); + +// // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP +// check_exact_one!(f(16093626, 69; f16) => b"1", 29); +// check_exact_one!(f( 9983778, 25; f16) => b"34", 15); +// check_exact_one!(f(12745034, 104; f16) => b"259", 39); +// check_exact_one!(f(12706553, 72; f16) => b"6001", 29); +// check_exact_one!(f(11005028, 45; f16) => b"38721", 21); +// check_exact_one!(f(15059547, 71; f16) => b"355584", 29); +// check_exact_one!(f(16015691, -99; f16) => b"2526831", -22); +// check_exact_one!(f( 8667859, 56; f16) => b"62458507", 24); +// check_exact_one!(f(14855922, -82; f16) => b"307213267", -17); +// check_exact_one!(f(14855922, -83; f16) => b"1536066333", -17); +// check_exact_one!(f(10144164, -110; f16) => b"78147796834", -26); +// check_exact_one!(f(13248074, 95; f16) => b"524810279937", 36); +// } + pub fn f32_shortest_sanity_test(mut f: F) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16),