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

Commit 2f770cd

Browse files
committed
Support parsing NaN and infinities from the hf* functions
This isn't very useful for constants since the trait constants are available, but does enable roundtripping via hex float syntax.
1 parent a89add3 commit 2f770cd

File tree

4 files changed

+61
-12
lines changed

4 files changed

+61
-12
lines changed

crates/libm-test/src/f8_impl.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
use std::cmp::{self, Ordering};
44
use std::{fmt, ops};
55

6+
use libm::support::hex_float::parse_any;
7+
68
use crate::Float;
79

810
/// Sometimes verifying float logic is easiest when all values can quickly be checked exhaustively
@@ -490,3 +492,7 @@ impl fmt::LowerHex for f8 {
490492
self.0.fmt(f)
491493
}
492494
}
495+
496+
pub const fn hf8(s: &str) -> f8 {
497+
f8(parse_any(s, 8, 3) as u8)
498+
}

crates/libm-test/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::path::PathBuf;
2020
use std::sync::LazyLock;
2121
use std::time::SystemTime;
2222

23-
pub use f8_impl::f8;
23+
pub use f8_impl::{f8, hf8};
2424
pub use libm::support::{Float, Int, IntTy, MinInt};
2525
pub use num::{FloatExt, linear_ints, logspace};
2626
pub use op::{

src/math/support/hex_float.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,25 @@ pub const fn hf128(s: &str) -> f128 {
2626
f128::from_bits(parse_any(s, 128, 112))
2727
}
2828

29-
const fn parse_any(s: &str, bits: u32, sig_bits: u32) -> u128 {
29+
/// Parse any float from hex to its bitwise representation.
30+
///
31+
/// `nan_repr` is passed rather than constructed so the platform-specific NaN is returned.
32+
pub const fn parse_any(s: &str, bits: u32, sig_bits: u32) -> u128 {
3033
let exp_bits: u32 = bits - sig_bits - 1;
3134
let max_msb: i32 = (1 << (exp_bits - 1)) - 1;
3235
// The exponent of one ULP in the subnormals
3336
let min_lsb: i32 = 1 - max_msb - sig_bits as i32;
3437

35-
let (neg, mut sig, exp) = parse_hex(s.as_bytes());
38+
let exp_mask = ((1 << exp_bits) - 1) << sig_bits;
3639

37-
if sig == 0 {
38-
return (neg as u128) << (bits - 1);
39-
}
40+
let (neg, mut sig, exp) = match parse_hex(s.as_bytes()) {
41+
Parsed::Finite { neg, sig: 0, .. } => return (neg as u128) << (bits - 1),
42+
Parsed::Finite { neg, sig, exp } => (neg, sig, exp),
43+
Parsed::Infinite { neg } => return ((neg as u128) << (bits - 1)) | exp_mask,
44+
Parsed::Nan { neg } => {
45+
return ((neg as u128) << (bits - 1)) | exp_mask | 1 << (sig_bits - 1);
46+
}
47+
};
4048

4149
// exponents of the least and most significant bits in the value
4250
let lsb = sig.trailing_zeros() as i32;
@@ -76,11 +84,24 @@ const fn parse_any(s: &str, bits: u32, sig_bits: u32) -> u128 {
7684
sig | ((neg as u128) << (bits - 1))
7785
}
7886

87+
/// A parsed floating point number.
88+
enum Parsed {
89+
/// Absolute value sig * 2^e
90+
Finite {
91+
neg: bool,
92+
sig: u128,
93+
exp: i32,
94+
},
95+
Infinite {
96+
neg: bool,
97+
},
98+
Nan {
99+
neg: bool,
100+
},
101+
}
102+
79103
/// Parse a hexadecimal float x
80-
/// returns (s,n,e):
81-
/// s == x.is_sign_negative()
82-
/// n * 2^e == x.abs()
83-
const fn parse_hex(mut b: &[u8]) -> (bool, u128, i32) {
104+
const fn parse_hex(mut b: &[u8]) -> Parsed {
84105
let mut neg = false;
85106
let mut sig: u128 = 0;
86107
let mut exp: i32 = 0;
@@ -90,6 +111,12 @@ const fn parse_hex(mut b: &[u8]) -> (bool, u128, i32) {
90111
neg = c == b'-';
91112
}
92113

114+
match *b {
115+
[b'i' | b'I', b'n' | b'N', b'f' | b'F'] => return Parsed::Infinite { neg },
116+
[b'n' | b'N', b'a' | b'A', b'n' | b'N'] => return Parsed::Nan { neg },
117+
_ => (),
118+
}
119+
93120
if let &[b'0', b'x' | b'X', ref rest @ ..] = b {
94121
b = rest;
95122
} else {
@@ -152,7 +179,7 @@ const fn parse_hex(mut b: &[u8]) -> (bool, u128, i32) {
152179
exp += pexp;
153180
}
154181

155-
(neg, sig, exp)
182+
Parsed::Finite { neg, sig, exp }
156183
}
157184

158185
const fn dec_digit(c: u8) -> u8 {
@@ -272,6 +299,10 @@ mod tests {
272299
("-0x1.998p-4", (-0.1f16).to_bits()),
273300
("0x0.123p-12", 0x0123),
274301
("0x1p-24", 0x0001),
302+
("nan", f16::NAN.to_bits()),
303+
("-nan", (-f16::NAN).to_bits()),
304+
("inf", f16::INFINITY.to_bits()),
305+
("-inf", f16::NEG_INFINITY.to_bits()),
275306
];
276307
for (s, exp) in checks {
277308
println!("parsing {s}");
@@ -322,6 +353,10 @@ mod tests {
322353
("0x1.111114p-127", 0x00444445),
323354
("0x1.23456p-130", 0x00091a2b),
324355
("0x1p-149", 0x00000001),
356+
("nan", f32::NAN.to_bits()),
357+
("-nan", (-f32::NAN).to_bits()),
358+
("inf", f32::INFINITY.to_bits()),
359+
("-inf", f32::NEG_INFINITY.to_bits()),
325360
];
326361
for (s, exp) in checks {
327362
println!("parsing {s}");
@@ -360,6 +395,10 @@ mod tests {
360395
("0x0.8000000000001p-1022", 0x0008000000000001),
361396
("0x0.123456789abcdp-1022", 0x000123456789abcd),
362397
("0x0.0000000000002p-1022", 0x0000000000000002),
398+
("nan", f64::NAN.to_bits()),
399+
("-nan", (-f64::NAN).to_bits()),
400+
("inf", f64::INFINITY.to_bits()),
401+
("-inf", f64::NEG_INFINITY.to_bits()),
363402
];
364403
for (s, exp) in checks {
365404
println!("parsing {s}");
@@ -401,6 +440,10 @@ mod tests {
401440
("-0x1.999999999999999999999999999ap-4", (-0.1f128).to_bits()),
402441
("0x0.abcdef0123456789abcdef012345p-16382", 0x0000abcdef0123456789abcdef012345),
403442
("0x1p-16494", 0x00000000000000000000000000000001),
443+
("nan", f128::NAN.to_bits()),
444+
("-nan", (-f128::NAN).to_bits()),
445+
("inf", f128::INFINITY.to_bits()),
446+
("-inf", f128::NEG_INFINITY.to_bits()),
404447
];
405448
for (s, exp) in checks {
406449
println!("parsing {s}");

src/math/support/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pub mod macros;
33
mod big;
44
mod float_traits;
5-
mod hex_float;
5+
pub mod hex_float;
66
mod int_traits;
77

88
#[allow(unused_imports)]

0 commit comments

Comments
 (0)