You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#![feature(f16, f128)]use hex_float::*;mod hex_float {usesuper::T;pubfnhf16(s:&str) -> f16{
f16::from_bits(parse_any(s,16,10)asu16)}/// Construct a 32-bit float from hex float representation (C-style)#[allow(unused)]pubfnhf32(s:&str) -> f32{
f32::from_bits(parse_any(s,32,23)asu32)}/// Construct a 64-bit float from hex float representation (C-style)pubfnhf64(s:&str) -> f64{
f64::from_bits(parse_any(s,64,52)asu64)}/// Parse any float from hex to its bitwise representation.////// `nan_repr` is passed rather than constructed so the platform-specific NaN is returned.pubfnparse_any(s:&str,bits:u32,sig_bits:u32) -> T{let exp_bits:u32 = bits - sig_bits - 1;let max_msb:i32 = (1 << (exp_bits - 1)) - 1;// The exponent of one ULP in the subnormalslet min_lsb:i32 = 1 - max_msb - sig_bits asi32;let exp_mask = ((1 << exp_bits) - 1) << sig_bits;let(neg,mut sig, exp) = matchparse_hex(s.as_bytes()){Parsed::Finite{ neg,sig:0, .. } => return(neg asT) << (bits - 1),Parsed::Finite{ neg, sig, exp } => (neg, sig, exp),Parsed::Infinite{ neg } => return((neg asT) << (bits - 1)) | exp_mask,Parsed::Nan{ neg } => {return((neg asT) << (bits - 1)) | exp_mask | (1 << (sig_bits - 1));}};// exponents of the least and most significant bits in the valuelet lsb = sig.trailing_zeros()asi32;let msb = u128_ilog2(sig)asi32;let sig_bits = sig_bits asi32;assert!(msb - lsb <= sig_bits,"the value is too precise");assert!(msb + exp <= max_msb,"the value is too huge");assert!(lsb + exp >= min_lsb,"the value is too tiny");// The parsed value is X = sig * 2^exp// Expressed as a multiple U of the smallest subnormal value:// X = U * 2^min_lsb, so U = sig * 2^(exp-min_lsb)letmut uexp = exp - min_lsb;let shift = if uexp + msb >= sig_bits {// normal, shift msb to position sig_bits
sig_bits - msb
}else{// subnormal, shift so that uexp becomes 0
uexp
};if shift >= 0{
sig <<= shift;}else{
sig >>= -shift;}
uexp -= shift;// the most significant bit is like having 1 in the exponent bits// add any leftover exponent to thatassert!(uexp >= 0 && uexp < (1 << exp_bits) - 2);
sig += (uexp asT) << sig_bits;// finally, set the sign bit if necessary
sig | ((neg asT) << (bits - 1))}/// A parsed floating point number.enumParsed{/// Absolute value sig * 2^eFinite{neg:bool,sig:T,exp:i32,},Infinite{neg:bool,},Nan{neg:bool,},}constfnu128_ilog2(v:T) -> u32{assert!(v != 0);T::BITS - 1 - v.leading_zeros()}/// Parse a hexadecimal float xconstfnparse_hex(mutb:&[u8]) -> Parsed{letmut neg = false;letmut sig:T = 0;letmut exp:i32 = 0;iflet&[c @ (b'-' | b'+'),ref rest @ ..] = b {
b = rest;
neg = c == b'-';}match*b {[b'i' | b'I',b'n' | b'N',b'f' | b'F'] => returnParsed::Infinite{ neg },[b'n' | b'N',b'a' | b'A',b'n' | b'N'] => returnParsed::Nan{ neg },
_ => (),}iflet&[b'0',b'x' | b'X',ref rest @ ..] = b {
b = rest;}else{panic!("no hex indicator");}letmut seen_point = false;letmut some_digits = false;whilelet&[c,ref rest @ ..] = b {
b = rest;match c {b'.' => {assert!(!seen_point);
seen_point = true;continue;}b'p' | b'P' => break,
c => {let digit = hex_digit(c);
some_digits = true;let of;(sig, of) = sig.overflowing_mul(16);assert!(!of,"too many digits");
sig |= digit asT;// up until the fractional point, the value grows// with more digits, but after it the exponent is// compensated to match.if seen_point {
exp -= 4;}}}}assert!(some_digits,"at least one digit is required");
some_digits = false;letmut negate_exp = false;iflet&[c @ (b'-' | b'+'),ref rest @ ..] = b {
b = rest;
negate_exp = c == b'-';}letmut pexp:i32 = 0;whilelet&[c,ref rest @ ..] = b {
b = rest;let digit = dec_digit(c);
some_digits = true;let of;(pexp, of) = pexp.overflowing_mul(10);assert!(!of,"too many exponent digits");
pexp += digit asi32;}assert!(some_digits,"at least one exponent digit is required");if negate_exp {
exp -= pexp;}else{
exp += pexp;}Parsed::Finite{ neg, sig, exp }}constfndec_digit(c:u8) -> u8{match c {b'0'..=b'9' => c - b'0',
_ => panic!("bad char"),}}constfnhex_digit(c:u8) -> u8{match c {b'0'..=b'9' => c - b'0',b'a'..=b'f' => c - b'a' + 10,b'A'..=b'F' => c - b'A' + 10,
_ => panic!("bad char"),}}}typeT = u64;fnmain(){letmut args = std::env::args();let _ = args.next();let a = args.next().unwrap_or_else(|| "0x1.92p+1".to_owned());let b = args.next().unwrap_or_else(|| "0x1.921fbp+1".to_owned());let c = args
.next().unwrap_or_else(|| "0x1.921fb54442d18p+1".to_owned());let n = 100_000;let t = std::time::Instant::now();letmut v = 0.0;for _ in0..n {
v += hf16(&a);}println!("16: {} {:?}", v > 10.0, t.elapsed());let t = std::time::Instant::now();letmut v = 0.0;for _ in0..n {
v += hf32(&b);}println!("32: {} {:?}", v > 10.0, t.elapsed());let t = std::time::Instant::now();letmut v = 0.0;for _ in0..n {
v += hf64(&c);}println!("64: {} {:?}", v > 10.0, t.elapsed());}
The text was updated successfully, but these errors were encountered:
CrazyboyQCD
changed the title
Seperate implementation of hex float parsing
Seperate implementation of hex float parsing for performance
Feb 20, 2025
The current implementation was primarily intended for compile-time parsing, so performance was not really a concern.
Since a generic integer type wouldn't work for const fn, I went with the current design for simplicity over using a macro-template, but indeed the parsing could be done with an integer the size of the target float.
I have plans and a mostly complete patch to implement proper error handling and correct rounding for this parsing. Once those are implemented, it will be better suited for runtime parsing so performance might be worth another look.
Side note: an ACP for hex parsing and printing was accepted at rust-lang/libs-team#536. I think it's probably a good idea to develop that here since it's easy to work on a small repo, then copy the implementations to rust-lang/rust once we're happy with it.
Currently hex float parsing use
u128
as storage for all float types, butf16
,f32
andf64
are fit inu64
, useu64
as storage give 25%~35% performance increase in simple test.https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=cd4e078bb43278130a09c522d8b24820
The text was updated successfully, but these errors were encountered: