Skip to content

Commit

Permalink
float: Add f16 parsing and printing
Browse files Browse the repository at this point in the history
  • Loading branch information
tgross35 committed Aug 25, 2024
1 parent 650e63f commit 21ffabc
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 14 deletions.
10 changes: 2 additions & 8 deletions library/core/src/fmt/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ macro_rules! impl_general_format {
}
}

impl_general_format! { f16 }
impl_general_format! { f32 f64 }

// Don't inline this so callers don't use the stack space this function
Expand Down Expand Up @@ -227,17 +228,10 @@ macro_rules! floating {
};
}

floating! { f16 }
floating! { f32 }
floating! { f64 }

#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for f16 {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{:#06x}", self.to_bits())
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for f128 {
#[inline]
Expand Down
44 changes: 44 additions & 0 deletions library/core/src/num/dec2flt/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,50 @@ pub trait RawFloat:
}
}

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 {
self.classify()
}
}

impl RawFloat for f32 {
type Int = u32;

Expand Down
2 changes: 2 additions & 0 deletions library/core/src/num/dec2flt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ macro_rules! from_str_float_impl {
}
};
}

from_str_float_impl!(f16);
from_str_float_impl!(f32);
from_str_float_impl!(f64);

Expand Down
6 changes: 6 additions & 0 deletions library/core/src/num/flt2dec/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ pub trait DecodableFloat: RawFloat + Copy {
fn min_pos_norm_value() -> Self;
}

impl DecodableFloat for f16 {
fn min_pos_norm_value() -> Self {
f16::MIN_POSITIVE
}
}

impl DecodableFloat for f32 {
fn min_pos_norm_value() -> Self {
f32::MIN_POSITIVE
Expand Down
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#![feature(error_generic_member_access)]
#![feature(exact_size_is_empty)]
#![feature(extern_types)]
#![feature(f16)]
#![feature(float_minimum_maximum)]
#![feature(flt2dec)]
#![feature(fmt_internals)]
Expand Down
16 changes: 16 additions & 0 deletions library/core/tests/num/dec2flt/float.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
use core::num::dec2flt::float::RawFloat;

#[test]
fn test_f16_integer_decode() {
assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1));
assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1));
assert_eq!(2f16.powf(4.0).integer_decode(), (1024, -6, 1));
assert_eq!(0f16.integer_decode(), (0, -25, 1));
assert_eq!((-0f16).integer_decode(), (0, -25, -1));
assert_eq!(f16::INFINITY.integer_decode(), (1024, 6, 1));
assert_eq!(f16::NEG_INFINITY.integer_decode(), (1024, 6, -1));

// Ignore the "sign" (quiet / signalling flag) of NAN.
// It can vary between runtime operations and LLVM folding.
let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode();
assert_eq!((nan_m, nan_p), (1536, 6));
}

#[test]
fn test_f32_integer_decode() {
assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
Expand Down
96 changes: 92 additions & 4 deletions library/core/tests/num/dec2flt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,103 @@ macro_rules! test_literal {
let x64: f64 = $x;
let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
for input in inputs {
assert_eq!(input.parse(), Ok(x64));
assert_eq!(input.parse(), Ok(x32));
assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
let neg_input = format!("-{input}");
assert_eq!(neg_input.parse(), Ok(-x64));
assert_eq!(neg_input.parse(), Ok(-x32));
assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
}
}};
}

// macro_rules! test_literal2 {
// ($x: expr) => {{
// // let x32: f32 = $x;
// let x64: f64 = $x;
// x::<f32>(stringify!($x));
// x::<f64>(stringify!($x));

// let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
// for input in inputs {
// println!("{}", input.parse::<f32>().unwrap());
// println!("{}", input.parse::<f64>().unwrap());
// // assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
// // assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
// // let neg_input = format!("-{input}");
// // assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
// // assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
// }
// }};
// }

// #[test]
// fn foo() {
// use core::num::dec2flt::float::RawFloat;
// use core::num::dec2flt::parse::parse_number;

// fn x<F: RawFloat + std::fmt::Display>(r: &str) {
// let mut s = r.as_bytes();
// let c = s[0];
// let negative = c == b'-';
// if c == b'-' || c == b'+' {
// s = &s[1..];
// }
// let mut num = parse_number(s).unwrap();
// num.negative = negative;
// if let Some(value) = num.try_fast_path::<F>() {
// // return Ok(value);
// println!("fast path {value}");
// return;
// }

// let q = num.exponent;
// let w = num.mantissa;

// println!(
// "float {r} {q} {w} {q:#066b} {w:#066b} sm10 {} lg10 {} ty {} chk {}",
// F::SMALLEST_POWER_OF_TEN,
// F::LARGEST_POWER_OF_TEN,
// std::any::type_name::<F>(),
// if w == 0 || q < F::SMALLEST_POWER_OF_TEN as i64 {
// "lt small 10"
// } else if q > F::LARGEST_POWER_OF_TEN as i64 {
// "gt big 10"
// } else {
// ""
// }
// );
// }

// // 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]
// fn foobar() {
// use core::num::dec2flt::float::RawFloat;
// panic!(
// "{} {} {} {}",
// <f32 as RawFloat>::LARGEST_POWER_OF_TEN,
// <f32 as RawFloat>::SMALLEST_POWER_OF_TEN,
// <f64 as RawFloat>::LARGEST_POWER_OF_TEN,
// <f64 as RawFloat>::SMALLEST_POWER_OF_TEN,
// )
// }

#[test]
fn ordinary() {
test_literal!(1.0);
Expand Down
101 changes: 101 additions & 0 deletions library/core/tests/num/flt2dec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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
}
Expand Down Expand Up @@ -176,6 +180,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()
Expand Down Expand Up @@ -226,6 +236,97 @@ 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

// pub fn f16_shortest_sanity_test<F>(mut f: F)
// where
// F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'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);
// }

// pub fn f16_exact_sanity_test<F>(mut f: F)
// where
// F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], 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<F>(mut f: F)
where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
Expand Down
3 changes: 3 additions & 0 deletions src/etc/test-float-parse/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(f16)]

mod traits;
mod ui;
mod validate;
Expand Down Expand Up @@ -114,6 +116,7 @@ pub fn register_tests(cfg: &Config) -> Vec<TestInfo> {
let mut tests = Vec::new();

// Register normal generators for all floats.
register_float::<f16>(&mut tests, cfg);
register_float::<f32>(&mut tests, cfg);
register_float::<f64>(&mut tests, cfg);

Expand Down
4 changes: 2 additions & 2 deletions src/etc/test-float-parse/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ macro_rules! impl_int {
}
}

impl_int!(u32, i32; u64, i64);
impl_int!(u16, i16; u32, i32; u64, i64);

/// Floating point types.
pub trait Float:
Expand Down Expand Up @@ -168,7 +168,7 @@ macro_rules! impl_float {
}
}

impl_float!(f32, u32; f64, u64;
impl_float!(f16, u16; f32, u32; f64, u64);

/// A test generator. Should provide an iterator that produces unique patterns to parse.
///
Expand Down

0 comments on commit 21ffabc

Please sign in to comment.