//! Parses hexadecimal float literals. //! There are two functions `parse_hexf32` and `parse_hexf64` provided for each type. //! //! ```rust //! use hexf_parse::*; //! assert_eq!(parse_hexf32("0x1.99999ap-4", false), Ok(0.1f32)); //! assert_eq!(parse_hexf64("0x1.999999999999ap-4", false), Ok(0.1f64)); //! ``` //! //! An additional `bool` parameter can be set to true if you want to allow underscores. //! //! ```rust //! use hexf_parse::*; //! assert!(parse_hexf64("0x0.1_7p8", false).is_err()); //! assert_eq!(parse_hexf64("0x0.1_7p8", true), Ok(23.0f64)); //! ``` //! //! The error is reported via an opaque `ParseHexfError` type.
use std::{f32, f64, fmt, isize, str};
/// An opaque error type from `parse_hexf32` and `parse_hexf64`. #[derive(Debug, Clone, PartialEq, Eq)] pubstruct ParseHexfError {
kind: ParseHexfErrorKind,
}
// 0[xX] if !(s.starts_with(b"0x") || s.starts_with(b"0X")) { return Err(INVALID);
}
// ([0-9a-fA-F][0-9a-fA-F_]*)? letmut s = &s[2..]; letmut acc = 0; // the accumulated mantissa letmut digit_seen = false; loop { let (s_, digit) = match s.split_first() {
Some((&c @ b'0'..=b'9', s)) => (s, c - b'0'),
Some((&c @ b'a'..=b'f', s)) => (s, c - b'a' + 10),
Some((&c @ b'A'..=b'F', s)) => (s, c - b'A' + 10),
Some((&b'_', s_)) if allow_underscore && digit_seen => {
s = s_; continue;
}
_ => break,
};
s = s_;
digit_seen = true;
// if `acc << 4` fails, mantissa definitely exceeds 64 bits so we should bail out if acc >> 60 != 0 { return Err(INEXACT);
}
acc = acc << 4 | digit as u64;
}
// (\.[0-9a-fA-F][0-9a-fA-F_]*)? // we want to ignore trailing zeroes but shifting at each digit will overflow first. // therefore we separately count the number of zeroes and flush it on non-zero digits. letmut nfracs = 0isize; // this is suboptimal but also practical, see below letmut nzeroes = 0isize; letmut frac_digit_seen = false; if s.starts_with(b".") {
s = &s[1..]; loop { let (s_, digit) = match s.split_first() {
Some((&c @ b'0'..=b'9', s)) => (s, c - b'0'),
Some((&c @ b'a'..=b'f', s)) => (s, c - b'a' + 10),
Some((&c @ b'A'..=b'F', s)) => (s, c - b'A' + 10),
Some((&b'_', s_)) if allow_underscore && frac_digit_seen => {
s = s_; continue;
}
_ => break,
};
// if the accumulator is non-zero, the shift cannot exceed 64 // (therefore the number of new digits cannot exceed 16). // this will catch e.g. `0.40000....00001` with sufficiently many zeroes if acc != 0 { if nnewdigits >= 16 || acc >> (64 - nnewdigits * 4) != 0 { return Err(INEXACT);
}
acc = acc << (nnewdigits * 4);
}
acc |= digit as u64;
}
}
}
// at least one digit should be present if !(digit_seen || frac_digit_seen) { return Err(INVALID);
}
// [pP] let s = match s.split_first() {
Some((&b'P', s)) | Some((&b'p', s)) => s,
_ => return Err(INVALID),
};
// [0-9_]*[0-9][0-9_]*$ letmut digit_seen = false; letmut exponent = 0isize; // this is suboptimal but also practical, see below loop { let (s_, digit) = match s.split_first() {
Some((&c @ b'0'..=b'9', s)) => (s, c - b'0'),
Some((&b'_', s_)) if allow_underscore => {
s = s_; continue;
}
None if digit_seen => break, // no more bytes expected, and at least one exponent digit should be present
_ => return Err(INVALID),
};
s = s_;
digit_seen = true;
// if we have no non-zero digits at this point, ignore the exponent :-) if acc != 0 {
exponent = exponent
.checked_mul(10)
.and_then(|v| v.checked_add(digit as isize))
.ok_or(INEXACT)?;
}
} if negative_exponent {
exponent = -exponent;
}
if acc == 0 { // ignore the exponent as above
Ok((negative, 0, 0))
} else { // the exponent should be biased by (nfracs * 4) to match with the mantissa read. // we still miss valid inputs like `0.0000...0001pX` where the input is filling // at least 1/4 of the total addressable memory, but I dare not handle them! let exponent = nfracs
.checked_mul(4)
.and_then(|v| exponent.checked_sub(v))
.ok_or(INEXACT)?;
Ok((negative, acc, exponent))
}
}
macro_rules! define_convert {
($name:ident => $f:ident) => { fn $name(negative: bool, mantissa: u64, exponent: isize) -> Result<$f, ParseHexfError> { // guard the exponent with the definitely safe range (we will exactly bound it later) if exponent < -0xffff || exponent > 0xffff { return Err(INEXACT);
}
// strip the trailing zeroes in mantissa and adjust exponent. // we do this because a unit in the least significant bit of mantissa is // always safe to represent while one in the most significant bit isn't. let trailing = mantissa.trailing_zeros() & 63; // guard mantissa=0 case let mantissa = mantissa >> trailing; let exponent = exponent + trailing as isize;
// normalize the exponent that the number is (1.xxxx * 2^normalexp), // and check for the mantissa and exponent ranges let leading = mantissa.leading_zeros(); let normalexp = exponent + (63 - leading as isize); let mantissasize = if normalexp < $f::MIN_EXP as isize - $f::MANTISSA_DIGITS as isize { // the number is smaller than the minimal denormal number return Err(INEXACT);
} elseif normalexp < ($f::MIN_EXP - 1) as isize { // the number is denormal, the # of bits in the mantissa is: // - minimum (1) at MIN_EXP - MANTISSA_DIGITS // - maximum (MANTISSA_DIGITS - 1) at MIN_EXP - 2
$f::MANTISSA_DIGITS as isize - $f::MIN_EXP as isize + normalexp + 1
} elseif normalexp < $f::MAX_EXP as isize { // the number is normal, the # of bits in the mantissa is fixed
$f::MANTISSA_DIGITS as isize
} else { // the number is larger than the maximal denormal number // ($f::MAX_EXP denotes NaN and infinities here) return Err(INEXACT);
};
if mantissa >> mantissasize == 0 { letmut mantissa = mantissa as $f; if negative {
mantissa = -mantissa;
} // yes, powi somehow does not work!
Ok(mantissa * (2.0as $f).powf(exponent as $f))
} else {
Err(INEXACT)
}
}
};
}
/// Tries to parse a hexadecimal float literal to `f32`. /// The underscore is allowed only when `allow_underscore` is true. pubfn parse_hexf32(s: &str, allow_underscore: bool) -> Result<f32, ParseHexfError> { let (negative, mantissa, exponent) = parse(s.as_bytes(), allow_underscore)?;
convert_hexf32(negative, mantissa, exponent)
}
/// Tries to parse a hexadecimal float literal to `f64`. /// The underscore is allowed only when `allow_underscore` is true. pubfn parse_hexf64(s: &str, allow_underscore: bool) -> Result<f64, ParseHexfError> { let (negative, mantissa, exponent) = parse(s.as_bytes(), allow_underscore)?;
convert_hexf64(negative, mantissa, exponent)
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.