Add hf16! and hf128!
Expand the existing hex float functions and macros with versions that work with `f16` and `f128`.
This commit is contained in:
parent
42e22132b4
commit
8dc4ef6f0f
3 changed files with 266 additions and 6 deletions
|
|
@ -4,6 +4,12 @@
|
|||
|
||||
use super::{f32_from_bits, f64_from_bits};
|
||||
|
||||
/// Construct a 16-bit float from hex float representation (C-style)
|
||||
#[cfg(f16_enabled)]
|
||||
pub const fn hf16(s: &str) -> f16 {
|
||||
f16::from_bits(parse_any(s, 16, 10) as u16)
|
||||
}
|
||||
|
||||
/// Construct a 32-bit float from hex float representation (C-style)
|
||||
pub const fn hf32(s: &str) -> f32 {
|
||||
f32_from_bits(parse_any(s, 32, 23) as u32)
|
||||
|
|
@ -14,6 +20,12 @@ pub const fn hf64(s: &str) -> f64 {
|
|||
f64_from_bits(parse_any(s, 64, 52) as u64)
|
||||
}
|
||||
|
||||
/// Construct a 128-bit float from hex float representation (C-style)
|
||||
#[cfg(f128_enabled)]
|
||||
pub const fn hf128(s: &str) -> f128 {
|
||||
f128::from_bits(parse_any(s, 128, 112))
|
||||
}
|
||||
|
||||
const fn parse_any(s: &str, bits: u32, sig_bits: u32) -> u128 {
|
||||
let exp_bits: u32 = bits - sig_bits - 1;
|
||||
let max_msb: i32 = (1 << (exp_bits - 1)) - 1;
|
||||
|
|
@ -230,6 +242,57 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
|
||||
// hide them from the AST.
|
||||
#[cfg(f16_enabled)]
|
||||
macro_rules! f16_tests {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_f16() {
|
||||
let checks = [
|
||||
("0x.1234p+16", (0x1234 as f16).to_bits()),
|
||||
("0x1.234p+12", (0x1234 as f16).to_bits()),
|
||||
("0x12.34p+8", (0x1234 as f16).to_bits()),
|
||||
("0x123.4p+4", (0x1234 as f16).to_bits()),
|
||||
("0x1234p+0", (0x1234 as f16).to_bits()),
|
||||
("0x1234.p+0", (0x1234 as f16).to_bits()),
|
||||
("0x1234.0p+0", (0x1234 as f16).to_bits()),
|
||||
("0x1.ffcp+15", f16::MAX.to_bits()),
|
||||
("0x1.0p+1", 2.0f16.to_bits()),
|
||||
("0x1.0p+0", 1.0f16.to_bits()),
|
||||
("0x1.ffp+8", 0x5ffc),
|
||||
("+0x1.ffp+8", 0x5ffc),
|
||||
("0x1p+0", 0x3c00),
|
||||
("0x1.998p-4", 0x2e66),
|
||||
("0x1.9p+6", 0x5640),
|
||||
("0x0.0p0", 0.0f16.to_bits()),
|
||||
("-0x0.0p0", (-0.0f16).to_bits()),
|
||||
("0x1.0p0", 1.0f16.to_bits()),
|
||||
("0x1.998p-4", (0.1f16).to_bits()),
|
||||
("-0x1.998p-4", (-0.1f16).to_bits()),
|
||||
("0x0.123p-12", 0x0123),
|
||||
("0x1p-24", 0x0001),
|
||||
];
|
||||
for (s, exp) in checks {
|
||||
println!("parsing {s}");
|
||||
let act = hf16(s).to_bits();
|
||||
assert_eq!(
|
||||
act, exp,
|
||||
"parsing {s}: {act:#06x} != {exp:#06x}\nact: {act:#018b}\nexp: {exp:#018b}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macros_f16() {
|
||||
assert_eq!(hf16!("0x1.ffp+8").to_bits(), 0x5ffc_u16);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(f16_enabled)]
|
||||
f16_tests!();
|
||||
|
||||
#[test]
|
||||
fn test_f32() {
|
||||
let checks = [
|
||||
|
|
@ -308,16 +371,67 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_f32_almost_extra_precision() {
|
||||
// Exact maximum precision allowed
|
||||
hf32("0x1.abcdeep+0");
|
||||
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
|
||||
// hide them from the AST.
|
||||
#[cfg(f128_enabled)]
|
||||
macro_rules! f128_tests {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_f128() {
|
||||
let checks = [
|
||||
("0x.1234p+16", (0x1234 as f128).to_bits()),
|
||||
("0x1.234p+12", (0x1234 as f128).to_bits()),
|
||||
("0x12.34p+8", (0x1234 as f128).to_bits()),
|
||||
("0x123.4p+4", (0x1234 as f128).to_bits()),
|
||||
("0x1234p+0", (0x1234 as f128).to_bits()),
|
||||
("0x1234.p+0", (0x1234 as f128).to_bits()),
|
||||
("0x1234.0p+0", (0x1234 as f128).to_bits()),
|
||||
("0x1.ffffffffffffffffffffffffffffp+16383", f128::MAX.to_bits()),
|
||||
("0x1.0p+1", 2.0f128.to_bits()),
|
||||
("0x1.0p+0", 1.0f128.to_bits()),
|
||||
("0x1.ffep+8", 0x4007ffe0000000000000000000000000),
|
||||
("+0x1.ffep+8", 0x4007ffe0000000000000000000000000),
|
||||
("0x1p+0", 0x3fff0000000000000000000000000000),
|
||||
("0x1.999999999999999999999999999ap-4", 0x3ffb999999999999999999999999999a),
|
||||
("0x1.9p+6", 0x40059000000000000000000000000000),
|
||||
("0x0.0p0", 0.0f128.to_bits()),
|
||||
("-0x0.0p0", (-0.0f128).to_bits()),
|
||||
("0x1.0p0", 1.0f128.to_bits()),
|
||||
("0x1.999999999999999999999999999ap-4", (0.1f128).to_bits()),
|
||||
("-0x1.999999999999999999999999999ap-4", (-0.1f128).to_bits()),
|
||||
("0x0.abcdef0123456789abcdef012345p-16382", 0x0000abcdef0123456789abcdef012345),
|
||||
("0x1p-16494", 0x00000000000000000000000000000001),
|
||||
];
|
||||
for (s, exp) in checks {
|
||||
println!("parsing {s}");
|
||||
let act = hf128(s).to_bits();
|
||||
assert_eq!(
|
||||
act, exp,
|
||||
"parsing {s}: {act:#034x} != {exp:#034x}\nact: {act:#0130b}\nexp: {exp:#0130b}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macros_f128() {
|
||||
assert_eq!(hf128!("0x1.ffep+8").to_bits(), 0x4007ffe0000000000000000000000000_u128);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(f128_enabled)]
|
||||
f128_tests!();
|
||||
|
||||
#[test]
|
||||
fn test_macros() {
|
||||
assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000u32);
|
||||
assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000u64);
|
||||
// FIXME(msrv): enable once parsing works
|
||||
// #[cfg(f16_enabled)]
|
||||
// assert_eq!(hf16!("0x1.ffp+8").to_bits(), 0x5ffc_u16);
|
||||
assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000_u32);
|
||||
assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000_u64);
|
||||
// FIXME(msrv): enable once parsing works
|
||||
// #[cfg(f128_enabled)]
|
||||
// assert_eq!(hf128!("0x1.ffep+8").to_bits(), 0x4007ffe0000000000000000000000000_u128);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -328,6 +442,69 @@ mod tests_panicking {
|
|||
extern crate std;
|
||||
use super::*;
|
||||
|
||||
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
|
||||
// hide them from the AST.
|
||||
#[cfg(f16_enabled)]
|
||||
macro_rules! f16_tests {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_f16_almost_extra_precision() {
|
||||
// Exact maximum precision allowed
|
||||
hf16("0x1.ffcp+0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too precise")]
|
||||
fn test_f16_extra_precision() {
|
||||
// One bit more than the above.
|
||||
hf16("0x1.ffdp+0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too huge")]
|
||||
fn test_f16_overflow() {
|
||||
// One bit more than the above.
|
||||
hf16("0x1p+16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_f16_tiniest() {
|
||||
let x = hf16("0x1.p-24");
|
||||
let y = hf16("0x0.001p-12");
|
||||
let z = hf16("0x0.8p-23");
|
||||
assert_eq!(x, y);
|
||||
assert_eq!(x, z);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too tiny")]
|
||||
fn test_f16_too_tiny() {
|
||||
hf16("0x1.p-25");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too tiny")]
|
||||
fn test_f16_also_too_tiny() {
|
||||
hf16("0x0.8p-24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too tiny")]
|
||||
fn test_f16_again_too_tiny() {
|
||||
hf16("0x0.001p-13");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(f16_enabled)]
|
||||
f16_tests!();
|
||||
|
||||
#[test]
|
||||
fn test_f32_almost_extra_precision() {
|
||||
// Exact maximum precision allowed
|
||||
hf32("0x1.abcdeep+0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_f32_extra_precision2() {
|
||||
|
|
@ -388,4 +565,61 @@ mod tests_panicking {
|
|||
// One bit more than the above.
|
||||
hf64("0x1.abcdabcdabcdf8p+0");
|
||||
}
|
||||
|
||||
// HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
|
||||
// hide them from the AST.
|
||||
#[cfg(f128_enabled)]
|
||||
macro_rules! f128_tests {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_f128_almost_extra_precision() {
|
||||
// Exact maximum precision allowed
|
||||
hf128("0x1.ffffffffffffffffffffffffffffp+16383");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too precise")]
|
||||
fn test_f128_extra_precision() {
|
||||
// One bit more than the above.
|
||||
hf128("0x1.ffffffffffffffffffffffffffff8p+16383");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too huge")]
|
||||
fn test_f128_overflow() {
|
||||
// One bit more than the above.
|
||||
hf128("0x1p+16384");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_f128_tiniest() {
|
||||
let x = hf128("0x1.p-16494");
|
||||
let y = hf128("0x0.0000000000000001p-16430");
|
||||
let z = hf128("0x0.8p-16493");
|
||||
assert_eq!(x, y);
|
||||
assert_eq!(x, z);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too tiny")]
|
||||
fn test_f128_too_tiny() {
|
||||
hf128("0x1.p-16495");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too tiny")]
|
||||
fn test_f128_again_too_tiny() {
|
||||
hf128("0x0.0000000000000001p-16431");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "the value is too tiny")]
|
||||
fn test_f128_also_too_tiny() {
|
||||
hf128("0x0.8p-16494");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(f128_enabled)]
|
||||
f128_tests!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,17 @@ macro_rules! select_implementation {
|
|||
(@cfg $provided:meta; $ex:expr) => { #[cfg($provided)] $ex };
|
||||
}
|
||||
|
||||
/// Construct a 16-bit float from hex float representation (C-style), guaranteed to
|
||||
/// evaluate at compile time.
|
||||
#[cfg(f16_enabled)]
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! hf16 {
|
||||
($s:literal) => {{
|
||||
const X: f16 = $crate::math::support::hf16($s);
|
||||
X
|
||||
}};
|
||||
}
|
||||
|
||||
/// Construct a 32-bit float from hex float representation (C-style), guaranteed to
|
||||
/// evaluate at compile time.
|
||||
#[allow(unused_macros)]
|
||||
|
|
@ -107,6 +118,17 @@ macro_rules! hf64 {
|
|||
}};
|
||||
}
|
||||
|
||||
/// Construct a 128-bit float from hex float representation (C-style), guaranteed to
|
||||
/// evaluate at compile time.
|
||||
#[cfg(f128_enabled)]
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! hf128 {
|
||||
($s:literal) => {{
|
||||
const X: f128 = $crate::math::support::hf128($s);
|
||||
X
|
||||
}};
|
||||
}
|
||||
|
||||
/// Assert `F::biteq` with better messages.
|
||||
#[cfg(test)]
|
||||
macro_rules! assert_biteq {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ mod int_traits;
|
|||
#[allow(unused_imports)]
|
||||
pub use float_traits::{Float, IntTy};
|
||||
pub(crate) use float_traits::{f32_from_bits, f64_from_bits};
|
||||
#[cfg(f16_enabled)]
|
||||
pub use hex_float::hf16;
|
||||
#[cfg(f128_enabled)]
|
||||
pub use hex_float::hf128;
|
||||
#[allow(unused_imports)]
|
||||
pub use hex_float::{hf32, hf64};
|
||||
pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue