Skip to content

Commit 35e8c9c

Browse files
committed
float: Add f16 parsing and printing
1 parent f2ffe95 commit 35e8c9c

File tree

11 files changed

+298
-14
lines changed

11 files changed

+298
-14
lines changed

library/core/src/fmt/float.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ macro_rules! impl_general_format {
2020
}
2121
}
2222

23+
impl_general_format! { f16 }
2324
impl_general_format! { f32 f64 }
2425

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

231+
floating! { f16 }
230232
floating! { f32 }
231233
floating! { f64 }
232234

233-
#[stable(feature = "rust1", since = "1.0.0")]
234-
impl Debug for f16 {
235-
#[inline]
236-
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
237-
write!(f, "{:#06x}", self.to_bits())
238-
}
239-
}
240-
241235
#[stable(feature = "rust1", since = "1.0.0")]
242236
impl Debug for f128 {
243237
#[inline]

library/core/src/num/dec2flt/float.rs

+44
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,50 @@ pub trait RawFloat:
191191
}
192192
}
193193

194+
impl RawFloat for f16 {
195+
type Int = u16;
196+
197+
const INFINITY: Self = Self::INFINITY;
198+
const NEG_INFINITY: Self = Self::NEG_INFINITY;
199+
const NAN: Self = Self::NAN;
200+
const NEG_NAN: Self = -Self::NAN;
201+
202+
const BITS: u32 = 16;
203+
const MANTISSA_BITS: u32 = Self::MANTISSA_DIGITS;
204+
const EXPONENT_MASK: Self::Int = Self::EXP_MASK;
205+
const MANTISSA_MASK: Self::Int = Self::MAN_MASK;
206+
207+
const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17;
208+
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10;
209+
// const SMALLEST_POWER_OF_TEN: i32 = -27;
210+
// const LARGEST_POWER_OF_TEN: i32 = Self::MAX_10_EXP;
211+
212+
#[inline]
213+
fn from_u64(v: u64) -> Self {
214+
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
215+
v as _
216+
}
217+
218+
#[inline]
219+
fn from_u64_bits(v: u64) -> Self {
220+
Self::from_bits((v & 0xFF) as u16)
221+
}
222+
223+
fn pow10_fast_path(exponent: usize) -> Self {
224+
#[allow(clippy::use_self)]
225+
const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.];
226+
TABLE[exponent & 15]
227+
}
228+
229+
fn to_bits(self) -> Self::Int {
230+
self.to_bits()
231+
}
232+
233+
fn classify(self) -> FpCategory {
234+
self.classify()
235+
}
236+
}
237+
194238
impl RawFloat for f32 {
195239
type Int = u32;
196240

library/core/src/num/dec2flt/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ macro_rules! from_str_float_impl {
171171
}
172172
};
173173
}
174+
175+
from_str_float_impl!(f16);
174176
from_str_float_impl!(f32);
175177
from_str_float_impl!(f64);
176178

library/core/src/num/flt2dec/decoder.rs

+6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ pub trait DecodableFloat: RawFloat + Copy {
4545
fn min_pos_norm_value() -> Self;
4646
}
4747

48+
impl DecodableFloat for f16 {
49+
fn min_pos_norm_value() -> Self {
50+
f16::MIN_POSITIVE
51+
}
52+
}
53+
4854
impl DecodableFloat for f32 {
4955
fn min_pos_norm_value() -> Self {
5056
f32::MIN_POSITIVE

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#![feature(error_generic_member_access)]
5252
#![feature(exact_size_is_empty)]
5353
#![feature(extern_types)]
54+
#![feature(f16)]
5455
#![feature(float_minimum_maximum)]
5556
#![feature(flt2dec)]
5657
#![feature(fmt_internals)]

library/core/tests/num/dec2flt/float.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
use core::num::dec2flt::float::RawFloat;
22

3+
#[test]
4+
fn test_f16_integer_decode() {
5+
assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1));
6+
assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1));
7+
assert_eq!(2f16.powf(4.0).integer_decode(), (1024, -6, 1));
8+
assert_eq!(0f16.integer_decode(), (0, -25, 1));
9+
assert_eq!((-0f16).integer_decode(), (0, -25, -1));
10+
assert_eq!(f16::INFINITY.integer_decode(), (1024, 6, 1));
11+
assert_eq!(f16::NEG_INFINITY.integer_decode(), (1024, 6, -1));
12+
13+
// Ignore the "sign" (quiet / signalling flag) of NAN.
14+
// It can vary between runtime operations and LLVM folding.
15+
let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode();
16+
assert_eq!((nan_m, nan_p), (1536, 6));
17+
}
18+
319
#[test]
420
fn test_f32_integer_decode() {
521
assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));

library/core/tests/num/dec2flt/lemire.rs

+24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
use core::num::dec2flt::lemire::compute_float;
22

3+
// fn compute_float16(q: i64, w: u64) -> (i32, u64) {
4+
// let fp = compute_float::<f16>(q, w);
5+
// (fp.p_biased, fp.m)
6+
// }
7+
38
fn compute_float32(q: i64, w: u64) -> (i32, u64) {
49
let fp = compute_float::<f32>(q, w);
510
(fp.p_biased, fp.m)
@@ -10,6 +15,25 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) {
1015
(fp.p_biased, fp.m)
1116
}
1217

18+
// #[test]
19+
// fn compute_float_f16_rounding() {
20+
// // These test near-halfway cases for single-precision floats.
21+
// assert_eq!(compute_float16(0, 16777216), (151, 0));
22+
// assert_eq!(compute_float16(0, 16777217), (151, 0));
23+
// assert_eq!(compute_float16(0, 16777218), (151, 1));
24+
// assert_eq!(compute_float16(0, 16777219), (151, 2));
25+
// assert_eq!(compute_float16(0, 16777220), (151, 2));
26+
27+
// // These are examples of the above tests, with
28+
// // digits from the exponent shifted to the mantissa.
29+
// assert_eq!(compute_float16(-10, 167772160000000000), (151, 0));
30+
// assert_eq!(compute_float16(-10, 167772170000000000), (151, 0));
31+
// assert_eq!(compute_float16(-10, 167772180000000000), (151, 1));
32+
// // Let's check the lines to see if anything is different in table...
33+
// assert_eq!(compute_float16(-10, 167772190000000000), (151, 2));
34+
// assert_eq!(compute_float16(-10, 167772200000000000), (151, 2));
35+
// }
36+
1337
#[test]
1438
fn compute_float_f32_rounding() {
1539
// These test near-halfway cases for single-precision floats.

library/core/tests/num/dec2flt/mod.rs

+97-4
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,112 @@ mod parse;
99
// Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32.
1010
macro_rules! test_literal {
1111
($x: expr) => {{
12+
let x16: f16 = $x;
1213
let x32: f32 = $x;
1314
let x64: f64 = $x;
1415
let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
16+
1517
for input in inputs {
16-
assert_eq!(input.parse(), Ok(x64));
17-
assert_eq!(input.parse(), Ok(x32));
18+
assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
19+
assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
20+
assert_eq!(input.parse(), Ok(x16), "failed f16 {input}");
21+
1822
let neg_input = format!("-{input}");
19-
assert_eq!(neg_input.parse(), Ok(-x64));
20-
assert_eq!(neg_input.parse(), Ok(-x32));
23+
assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
24+
assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
25+
assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}");
2126
}
2227
}};
2328
}
2429

30+
// macro_rules! test_literal2 {
31+
// ($x: expr) => {{
32+
// // let x32: f32 = $x;
33+
// let x64: f64 = $x;
34+
// x::<f32>(stringify!($x));
35+
// x::<f64>(stringify!($x));
36+
37+
// let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
38+
// for input in inputs {
39+
// println!("{}", input.parse::<f32>().unwrap());
40+
// println!("{}", input.parse::<f64>().unwrap());
41+
// // assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
42+
// // assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
43+
// // let neg_input = format!("-{input}");
44+
// // assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
45+
// // assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
46+
// }
47+
// }};
48+
// }
49+
50+
// #[test]
51+
// fn foo() {
52+
// use core::num::dec2flt::float::RawFloat;
53+
// use core::num::dec2flt::parse::parse_number;
54+
55+
// fn x<F: RawFloat + std::fmt::Display>(r: &str) {
56+
// let mut s = r.as_bytes();
57+
// let c = s[0];
58+
// let negative = c == b'-';
59+
// if c == b'-' || c == b'+' {
60+
// s = &s[1..];
61+
// }
62+
// let mut num = parse_number(s).unwrap();
63+
// num.negative = negative;
64+
// if let Some(value) = num.try_fast_path::<F>() {
65+
// // return Ok(value);
66+
// println!("fast path {value}");
67+
// return;
68+
// }
69+
70+
// let q = num.exponent;
71+
// let w = num.mantissa;
72+
73+
// println!(
74+
// "float {r} {q} {w} {q:#066b} {w:#066b} sm10 {} lg10 {} ty {} chk {}",
75+
// F::SMALLEST_POWER_OF_TEN,
76+
// F::LARGEST_POWER_OF_TEN,
77+
// std::any::type_name::<F>(),
78+
// if w == 0 || q < F::SMALLEST_POWER_OF_TEN as i64 {
79+
// "lt small 10"
80+
// } else if q > F::LARGEST_POWER_OF_TEN as i64 {
81+
// "gt big 10"
82+
// } else {
83+
// ""
84+
// }
85+
// );
86+
// }
87+
88+
// // test_literal2!(1e-20);
89+
// // test_literal2!(1e-30);
90+
// // test_literal2!(1e-40);
91+
// // test_literal2!(1e-50);
92+
// // test_literal2!(1e-60);
93+
// // test_literal2!(1e-63);
94+
// // test_literal2!(1e-64);
95+
// // test_literal2!(1e-65);
96+
// // test_literal2!(1e-66);
97+
// // test_literal2!(1e-70);
98+
// // test_literal2!(1e-70);
99+
// // test_literal2!(1e-70);
100+
// // test_literal2!(1e-70);
101+
// // test_literal2!(2.225073858507201136057409796709131975934819546351645648023426109724822222021076945516529523908135087914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630666655994938275772572015763062690663332647565300009245888316433037779791869612049497390377829704905051080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884423900265498198385487948292206894721689831099698365846814022854243330660339850886445804001034933970427567186443383770486037861622771738545623065874679014086723327636718749999999999999999999999999999999999999e-308);
102+
// // test_literal2!(1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38);
103+
// // panic!();
104+
// }
105+
106+
// #[test]
107+
// fn foobar() {
108+
// use core::num::dec2flt::float::RawFloat;
109+
// panic!(
110+
// "{} {} {} {}",
111+
// <f32 as RawFloat>::LARGEST_POWER_OF_TEN,
112+
// <f32 as RawFloat>::SMALLEST_POWER_OF_TEN,
113+
// <f64 as RawFloat>::LARGEST_POWER_OF_TEN,
114+
// <f64 as RawFloat>::SMALLEST_POWER_OF_TEN,
115+
// )
116+
// }
117+
25118
#[test]
26119
fn ordinary() {
27120
test_literal!(1.0);

0 commit comments

Comments
 (0)