Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit eb29467

Browse files
committed
Add hf16! and hf128!
Expand the existing hex float functions and macros with versions that work with `f16` and `f128`.
1 parent bc6a615 commit eb29467

File tree

3 files changed

+266
-6
lines changed

3 files changed

+266
-6
lines changed

src/math/support/hex_float.rs

Lines changed: 240 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
use super::{f32_from_bits, f64_from_bits};
66

7+
/// Construct a 16-bit float from hex float representation (C-style)
8+
#[cfg(f16_enabled)]
9+
pub const fn hf16(s: &str) -> f16 {
10+
f16::from_bits(parse_any(s, 16, 10) as u16)
11+
}
12+
713
/// Construct a 32-bit float from hex float representation (C-style)
814
pub const fn hf32(s: &str) -> f32 {
915
f32_from_bits(parse_any(s, 32, 23) as u32)
@@ -14,6 +20,12 @@ pub const fn hf64(s: &str) -> f64 {
1420
f64_from_bits(parse_any(s, 64, 52) as u64)
1521
}
1622

23+
/// Construct a 128-bit float from hex float representation (C-style)
24+
#[cfg(f128_enabled)]
25+
pub const fn hf128(s: &str) -> f128 {
26+
f128::from_bits(parse_any(s, 128, 112))
27+
}
28+
1729
const fn parse_any(s: &str, bits: u32, sig_bits: u32) -> u128 {
1830
let exp_bits: u32 = bits - sig_bits - 1;
1931
let max_msb: i32 = (1 << (exp_bits - 1)) - 1;
@@ -230,6 +242,57 @@ mod tests {
230242
}
231243
}
232244

245+
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
246+
// hide them from the AST.
247+
#[cfg(f16_enabled)]
248+
macro_rules! f16_tests {
249+
() => {
250+
#[test]
251+
fn test_f16() {
252+
let checks = [
253+
("0x.1234p+16", (0x1234 as f16).to_bits()),
254+
("0x1.234p+12", (0x1234 as f16).to_bits()),
255+
("0x12.34p+8", (0x1234 as f16).to_bits()),
256+
("0x123.4p+4", (0x1234 as f16).to_bits()),
257+
("0x1234p+0", (0x1234 as f16).to_bits()),
258+
("0x1234.p+0", (0x1234 as f16).to_bits()),
259+
("0x1234.0p+0", (0x1234 as f16).to_bits()),
260+
("0x1.ffcp+15", f16::MAX.to_bits()),
261+
("0x1.0p+1", 2.0f16.to_bits()),
262+
("0x1.0p+0", 1.0f16.to_bits()),
263+
("0x1.ffp+8", 0x5ffc),
264+
("+0x1.ffp+8", 0x5ffc),
265+
("0x1p+0", 0x3c00),
266+
("0x1.998p-4", 0x2e66),
267+
("0x1.9p+6", 0x5640),
268+
("0x0.0p0", 0.0f16.to_bits()),
269+
("-0x0.0p0", (-0.0f16).to_bits()),
270+
("0x1.0p0", 1.0f16.to_bits()),
271+
("0x1.998p-4", (0.1f16).to_bits()),
272+
("-0x1.998p-4", (-0.1f16).to_bits()),
273+
("0x0.123p-12", 0x0123),
274+
("0x1p-24", 0x0001),
275+
];
276+
for (s, exp) in checks {
277+
println!("parsing {s}");
278+
let act = hf16(s).to_bits();
279+
assert_eq!(
280+
act, exp,
281+
"parsing {s}: {act:#06x} != {exp:#06x}\nact: {act:#018b}\nexp: {exp:#018b}"
282+
);
283+
}
284+
}
285+
286+
#[test]
287+
fn test_macros_f16() {
288+
assert_eq!(hf16!("0x1.ffp+8").to_bits(), 0x5ffc_u16);
289+
}
290+
};
291+
}
292+
293+
#[cfg(f16_enabled)]
294+
f16_tests!();
295+
233296
#[test]
234297
fn test_f32() {
235298
let checks = [
@@ -308,16 +371,67 @@ mod tests {
308371
}
309372
}
310373

311-
#[test]
312-
fn test_f32_almost_extra_precision() {
313-
// Exact maximum precision allowed
314-
hf32("0x1.abcdeep+0");
374+
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
375+
// hide them from the AST.
376+
#[cfg(f128_enabled)]
377+
macro_rules! f128_tests {
378+
() => {
379+
#[test]
380+
fn test_f128() {
381+
let checks = [
382+
("0x.1234p+16", (0x1234 as f128).to_bits()),
383+
("0x1.234p+12", (0x1234 as f128).to_bits()),
384+
("0x12.34p+8", (0x1234 as f128).to_bits()),
385+
("0x123.4p+4", (0x1234 as f128).to_bits()),
386+
("0x1234p+0", (0x1234 as f128).to_bits()),
387+
("0x1234.p+0", (0x1234 as f128).to_bits()),
388+
("0x1234.0p+0", (0x1234 as f128).to_bits()),
389+
("0x1.ffffffffffffffffffffffffffffp+16383", f128::MAX.to_bits()),
390+
("0x1.0p+1", 2.0f128.to_bits()),
391+
("0x1.0p+0", 1.0f128.to_bits()),
392+
("0x1.ffep+8", 0x4007ffe0000000000000000000000000),
393+
("+0x1.ffep+8", 0x4007ffe0000000000000000000000000),
394+
("0x1p+0", 0x3fff0000000000000000000000000000),
395+
("0x1.999999999999999999999999999ap-4", 0x3ffb999999999999999999999999999a),
396+
("0x1.9p+6", 0x40059000000000000000000000000000),
397+
("0x0.0p0", 0.0f128.to_bits()),
398+
("-0x0.0p0", (-0.0f128).to_bits()),
399+
("0x1.0p0", 1.0f128.to_bits()),
400+
("0x1.999999999999999999999999999ap-4", (0.1f128).to_bits()),
401+
("-0x1.999999999999999999999999999ap-4", (-0.1f128).to_bits()),
402+
("0x0.abcdef0123456789abcdef012345p-16382", 0x0000abcdef0123456789abcdef012345),
403+
("0x1p-16494", 0x00000000000000000000000000000001),
404+
];
405+
for (s, exp) in checks {
406+
println!("parsing {s}");
407+
let act = hf128(s).to_bits();
408+
assert_eq!(
409+
act, exp,
410+
"parsing {s}: {act:#034x} != {exp:#034x}\nact: {act:#0130b}\nexp: {exp:#0130b}"
411+
);
412+
}
413+
}
414+
415+
#[test]
416+
fn test_macros_f128() {
417+
assert_eq!(hf128!("0x1.ffep+8").to_bits(), 0x4007ffe0000000000000000000000000_u128);
418+
}
419+
}
315420
}
316421

422+
#[cfg(f128_enabled)]
423+
f128_tests!();
424+
317425
#[test]
318426
fn test_macros() {
319-
assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000u32);
320-
assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000u64);
427+
// FIXME(msrv): enable once parsing works
428+
// #[cfg(f16_enabled)]
429+
// assert_eq!(hf16!("0x1.ffp+8").to_bits(), 0x5ffc_u16);
430+
assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000_u32);
431+
assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000_u64);
432+
// FIXME(msrv): enable once parsing works
433+
// #[cfg(f128_enabled)]
434+
// assert_eq!(hf128!("0x1.ffep+8").to_bits(), 0x4007ffe0000000000000000000000000_u128);
321435
}
322436
}
323437

@@ -328,6 +442,69 @@ mod tests_panicking {
328442
extern crate std;
329443
use super::*;
330444

445+
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
446+
// hide them from the AST.
447+
#[cfg(f16_enabled)]
448+
macro_rules! f16_tests {
449+
() => {
450+
#[test]
451+
fn test_f16_almost_extra_precision() {
452+
// Exact maximum precision allowed
453+
hf16("0x1.ffcp+0");
454+
}
455+
456+
#[test]
457+
#[should_panic(expected = "the value is too precise")]
458+
fn test_f16_extra_precision() {
459+
// One bit more than the above.
460+
hf16("0x1.ffdp+0");
461+
}
462+
463+
#[test]
464+
#[should_panic(expected = "the value is too huge")]
465+
fn test_f16_overflow() {
466+
// One bit more than the above.
467+
hf16("0x1p+16");
468+
}
469+
470+
#[test]
471+
fn test_f16_tiniest() {
472+
let x = hf16("0x1.p-24");
473+
let y = hf16("0x0.001p-12");
474+
let z = hf16("0x0.8p-23");
475+
assert_eq!(x, y);
476+
assert_eq!(x, z);
477+
}
478+
479+
#[test]
480+
#[should_panic(expected = "the value is too tiny")]
481+
fn test_f16_too_tiny() {
482+
hf16("0x1.p-25");
483+
}
484+
485+
#[test]
486+
#[should_panic(expected = "the value is too tiny")]
487+
fn test_f16_also_too_tiny() {
488+
hf16("0x0.8p-24");
489+
}
490+
491+
#[test]
492+
#[should_panic(expected = "the value is too tiny")]
493+
fn test_f16_again_too_tiny() {
494+
hf16("0x0.001p-13");
495+
}
496+
};
497+
}
498+
499+
#[cfg(f16_enabled)]
500+
f16_tests!();
501+
502+
#[test]
503+
fn test_f32_almost_extra_precision() {
504+
// Exact maximum precision allowed
505+
hf32("0x1.abcdeep+0");
506+
}
507+
331508
#[test]
332509
#[should_panic]
333510
fn test_f32_extra_precision2() {
@@ -388,4 +565,61 @@ mod tests_panicking {
388565
// One bit more than the above.
389566
hf64("0x1.abcdabcdabcdf8p+0");
390567
}
568+
569+
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
570+
// hide them from the AST.
571+
#[cfg(f128_enabled)]
572+
macro_rules! f128_tests {
573+
() => {
574+
#[test]
575+
fn test_f128_almost_extra_precision() {
576+
// Exact maximum precision allowed
577+
hf128("0x1.ffffffffffffffffffffffffffffp+16383");
578+
}
579+
580+
#[test]
581+
#[should_panic(expected = "the value is too precise")]
582+
fn test_f128_extra_precision() {
583+
// One bit more than the above.
584+
hf128("0x1.ffffffffffffffffffffffffffff8p+16383");
585+
}
586+
587+
#[test]
588+
#[should_panic(expected = "the value is too huge")]
589+
fn test_f128_overflow() {
590+
// One bit more than the above.
591+
hf128("0x1p+16384");
592+
}
593+
594+
#[test]
595+
fn test_f128_tiniest() {
596+
let x = hf128("0x1.p-16494");
597+
let y = hf128("0x0.0000000000000001p-16430");
598+
let z = hf128("0x0.8p-16493");
599+
assert_eq!(x, y);
600+
assert_eq!(x, z);
601+
}
602+
603+
#[test]
604+
#[should_panic(expected = "the value is too tiny")]
605+
fn test_f128_too_tiny() {
606+
hf128("0x1.p-16495");
607+
}
608+
609+
#[test]
610+
#[should_panic(expected = "the value is too tiny")]
611+
fn test_f128_again_too_tiny() {
612+
hf128("0x0.0000000000000001p-16431");
613+
}
614+
615+
#[test]
616+
#[should_panic(expected = "the value is too tiny")]
617+
fn test_f128_also_too_tiny() {
618+
hf128("0x0.8p-16494");
619+
}
620+
};
621+
}
622+
623+
#[cfg(f128_enabled)]
624+
f128_tests!();
391625
}

src/math/support/macros.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ macro_rules! select_implementation {
8787
(@cfg $provided:meta; $ex:expr) => { #[cfg($provided)] $ex };
8888
}
8989

90+
/// Construct a 16-bit float from hex float representation (C-style), guaranteed to
91+
/// evaluate at compile time.
92+
#[cfg(f16_enabled)]
93+
#[allow(unused_macros)]
94+
macro_rules! hf16 {
95+
($s:literal) => {{
96+
const X: f16 = $crate::math::support::hf16($s);
97+
X
98+
}};
99+
}
100+
90101
/// Construct a 32-bit float from hex float representation (C-style), guaranteed to
91102
/// evaluate at compile time.
92103
#[allow(unused_macros)]
@@ -107,6 +118,17 @@ macro_rules! hf64 {
107118
}};
108119
}
109120

121+
/// Construct a 128-bit float from hex float representation (C-style), guaranteed to
122+
/// evaluate at compile time.
123+
#[cfg(f128_enabled)]
124+
#[allow(unused_macros)]
125+
macro_rules! hf128 {
126+
($s:literal) => {{
127+
const X: f128 = $crate::math::support::hf128($s);
128+
X
129+
}};
130+
}
131+
110132
/// Assert `F::biteq` with better messages.
111133
#[cfg(test)]
112134
macro_rules! assert_biteq {

src/math/support/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ mod int_traits;
88
#[allow(unused_imports)]
99
pub use float_traits::{Float, IntTy};
1010
pub(crate) use float_traits::{f32_from_bits, f64_from_bits};
11+
#[cfg(f16_enabled)]
12+
pub use hex_float::hf16;
13+
#[cfg(f128_enabled)]
14+
pub use hex_float::hf128;
1115
#[allow(unused_imports)]
1216
pub use hex_float::{hf32, hf64};
1317
pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt};

0 commit comments

Comments
 (0)