summaryrefslogtreecommitdiffstats
path: root/third_party/rust/hexf-parse
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/rust/hexf-parse
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/hexf-parse')
-rw-r--r--third_party/rust/hexf-parse/.cargo-checksum.json1
-rw-r--r--third_party/rust/hexf-parse/Cargo.toml22
-rw-r--r--third_party/rust/hexf-parse/src/lib.rs533
3 files changed, 556 insertions, 0 deletions
diff --git a/third_party/rust/hexf-parse/.cargo-checksum.json b/third_party/rust/hexf-parse/.cargo-checksum.json
new file mode 100644
index 0000000000..e02bbfb87a
--- /dev/null
+++ b/third_party/rust/hexf-parse/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"dd7274968556f3f3309df5615d94437ec4e4823777cc440f91177b2170cbe2a7","src/lib.rs":"511b164642efe470e88323b8afe0274e797805ff2441f0173b5b005ad4b4d1b1"},"package":"dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"} \ No newline at end of file
diff --git a/third_party/rust/hexf-parse/Cargo.toml b/third_party/rust/hexf-parse/Cargo.toml
new file mode 100644
index 0000000000..31287dc85a
--- /dev/null
+++ b/third_party/rust/hexf-parse/Cargo.toml
@@ -0,0 +1,22 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "hexf-parse"
+version = "0.2.1"
+authors = ["Kang Seonghoon <public+rust@mearie.org>"]
+description = "Parses hexadecimal floats (see also hexf)"
+homepage = "https://github.com/lifthrasiir/hexf"
+documentation = "https://docs.rs/hexf-parse/"
+license = "CC0-1.0"
+repository = "https://github.com/lifthrasiir/hexf"
diff --git a/third_party/rust/hexf-parse/src/lib.rs b/third_party/rust/hexf-parse/src/lib.rs
new file mode 100644
index 0000000000..60439bb5be
--- /dev/null
+++ b/third_party/rust/hexf-parse/src/lib.rs
@@ -0,0 +1,533 @@
+//! 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)]
+pub struct ParseHexfError {
+ kind: ParseHexfErrorKind,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum ParseHexfErrorKind {
+ Empty,
+ Invalid,
+ Inexact,
+}
+
+const EMPTY: ParseHexfError = ParseHexfError {
+ kind: ParseHexfErrorKind::Empty,
+};
+const INVALID: ParseHexfError = ParseHexfError {
+ kind: ParseHexfErrorKind::Invalid,
+};
+const INEXACT: ParseHexfError = ParseHexfError {
+ kind: ParseHexfErrorKind::Inexact,
+};
+
+impl ParseHexfError {
+ fn text(&self) -> &'static str {
+ match self.kind {
+ ParseHexfErrorKind::Empty => "cannot parse float from empty string",
+ ParseHexfErrorKind::Invalid => "invalid hexadecimal float literal",
+ ParseHexfErrorKind::Inexact => "cannot exactly represent float in target type",
+ }
+ }
+}
+
+impl fmt::Display for ParseHexfError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self.text(), f)
+ }
+}
+
+impl std::error::Error for ParseHexfError {
+ fn description(&self) -> &'static str {
+ self.text()
+ }
+}
+
+fn parse(s: &[u8], allow_underscore: bool) -> Result<(bool, u64, isize), ParseHexfError> {
+ // ^[+-]?
+ let (s, negative) = match s.split_first() {
+ Some((&b'+', s)) => (s, false),
+ Some((&b'-', s)) => (s, true),
+ Some(_) => (s, false),
+ None => return Err(EMPTY),
+ };
+
+ // 0[xX]
+ if !(s.starts_with(b"0x") || s.starts_with(b"0X")) {
+ return Err(INVALID);
+ }
+
+ // ([0-9a-fA-F][0-9a-fA-F_]*)?
+ let mut s = &s[2..];
+ let mut acc = 0; // the accumulated mantissa
+ let mut 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.
+ let mut nfracs = 0isize; // this is suboptimal but also practical, see below
+ let mut nzeroes = 0isize;
+ let mut 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,
+ };
+
+ s = s_;
+ frac_digit_seen = true;
+
+ if digit == 0 {
+ nzeroes = nzeroes.checked_add(1).ok_or(INEXACT)?;
+ } else {
+ // flush nzeroes
+ let nnewdigits = nzeroes.checked_add(1).ok_or(INEXACT)?;
+ nfracs = nfracs.checked_add(nnewdigits).ok_or(INEXACT)?;
+ nzeroes = 0;
+
+ // 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),
+ };
+
+ // [+-]?
+ let (mut s, negative_exponent) = match s.split_first() {
+ Some((&b'+', s)) => (s, false),
+ Some((&b'-', s)) => (s, true),
+ Some(_) => (s, false),
+ None => return Err(INVALID),
+ };
+
+ // [0-9_]*[0-9][0-9_]*$
+ let mut digit_seen = false;
+ let mut 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))
+ }
+}
+
+#[test]
+fn test_parse() {
+ assert_eq!(parse(b"", false), Err(EMPTY));
+ assert_eq!(parse(b" ", false), Err(INVALID));
+ assert_eq!(parse(b"3.14", false), Err(INVALID));
+ assert_eq!(parse(b"0x3.14", false), Err(INVALID));
+ assert_eq!(parse(b"0x3.14fp+3", false), Ok((false, 0x314f, 3 - 12)));
+ assert_eq!(parse(b" 0x3.14p+3", false), Err(INVALID));
+ assert_eq!(parse(b"0x3.14p+3 ", false), Err(INVALID));
+ assert_eq!(parse(b"+0x3.14fp+3", false), Ok((false, 0x314f, 3 - 12)));
+ assert_eq!(parse(b"-0x3.14fp+3", false), Ok((true, 0x314f, 3 - 12)));
+ assert_eq!(parse(b"0xAbC.p1", false), Ok((false, 0xabc, 1)));
+ assert_eq!(parse(b"0x0.7p1", false), Ok((false, 0x7, 1 - 4)));
+ assert_eq!(parse(b"0x.dEfP-1", false), Ok((false, 0xdef, -1 - 12)));
+ assert_eq!(parse(b"0x.p1", false), Err(INVALID));
+ assert_eq!(parse(b"0x.P1", false), Err(INVALID));
+ assert_eq!(parse(b"0xp1", false), Err(INVALID));
+ assert_eq!(parse(b"0xP1", false), Err(INVALID));
+ assert_eq!(parse(b"0x0p", false), Err(INVALID));
+ assert_eq!(parse(b"0xp", false), Err(INVALID));
+ assert_eq!(parse(b"0x.p", false), Err(INVALID));
+ assert_eq!(parse(b"0x0p1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0P1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0.p1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0.P1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0.0p1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0.0P1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x.0p1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x.0P1", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0p0", false), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0.p999999999", false), Ok((false, 0, 0)));
+ assert_eq!(
+ parse(b"0x0.p99999999999999999999999999999", false),
+ Ok((false, 0, 0))
+ );
+ assert_eq!(
+ parse(b"0x0.p-99999999999999999999999999999", false),
+ Ok((false, 0, 0))
+ );
+ assert_eq!(
+ parse(b"0x1.p99999999999999999999999999999", false),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ parse(b"0x1.p-99999999999999999999999999999", false),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ parse(b"0x4.00000000000000000000p55", false),
+ Ok((false, 4, 55))
+ );
+ assert_eq!(
+ parse(b"0x4.00001000000000000000p55", false),
+ Ok((false, 0x400001, 55 - 20))
+ );
+ assert_eq!(parse(b"0x4.00000000000000000001p55", false), Err(INEXACT));
+
+ // underscore insertion
+ assert_eq!(
+ parse(b"-0x3____.1_4___p+___5___", true),
+ Ok((true, 0x314, 5 - 8))
+ );
+ assert_eq!(parse(b"-_0x3.14p+5", true), Err(INVALID));
+ assert_eq!(parse(b"_0x3.14p+5", true), Err(INVALID));
+ assert_eq!(parse(b"0x_3.14p+5", true), Err(INVALID));
+ assert_eq!(parse(b"0x3._14p+5", true), Err(INVALID));
+ assert_eq!(parse(b"0x3.14p_+5", true), Err(INVALID));
+ assert_eq!(parse(b"-0x____.1_4___p+___5___", true), Err(INVALID));
+ assert_eq!(parse(b"-0x3____.____p+___5___", true), Err(INVALID));
+ assert_eq!(parse(b"-0x3____.1_4___p+______", true), Err(INVALID));
+ assert_eq!(parse(b"0x_p0", false), Err(INVALID));
+ assert_eq!(parse(b"0x_0p0", true), Err(INVALID));
+ assert_eq!(parse(b"0x_p0", true), Err(INVALID));
+ assert_eq!(parse(b"0x._p0", true), Err(INVALID));
+ assert_eq!(parse(b"0x._0p0", true), Err(INVALID));
+ assert_eq!(parse(b"0x0._0p0", true), Err(INVALID));
+ assert_eq!(parse(b"0x0_p0", true), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x.0_p0", true), Ok((false, 0, 0)));
+ assert_eq!(parse(b"0x0.0_p0", true), Ok((false, 0, 0)));
+
+ // issues
+ // #11 (https://github.com/lifthrasiir/hexf/issues/11)
+ assert_eq!(parse(b"0x1p-149", false), parse(b"0x1.0p-149", false));
+}
+
+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);
+ } else if 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
+ } else if 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 {
+ let mut mantissa = mantissa as $f;
+ if negative {
+ mantissa = -mantissa;
+ }
+ // yes, powi somehow does not work!
+ Ok(mantissa * (2.0 as $f).powf(exponent as $f))
+ } else {
+ Err(INEXACT)
+ }
+ }
+ };
+}
+
+define_convert!(convert_hexf32 => f32);
+define_convert!(convert_hexf64 => f64);
+
+#[test]
+fn test_convert_hexf32() {
+ assert_eq!(convert_hexf32(false, 0, 0), Ok(0.0));
+ assert_eq!(convert_hexf32(false, 1, 0), Ok(1.0));
+ assert_eq!(convert_hexf32(false, 10, 0), Ok(10.0));
+ assert_eq!(convert_hexf32(false, 10, 1), Ok(20.0));
+ assert_eq!(convert_hexf32(false, 10, -1), Ok(5.0));
+ assert_eq!(convert_hexf32(true, 0, 0), Ok(-0.0));
+ assert_eq!(convert_hexf32(true, 1, 0), Ok(-1.0));
+
+ // negative zeroes
+ assert_eq!(convert_hexf32(false, 0, 0).unwrap().signum(), 1.0);
+ assert_eq!(convert_hexf32(true, 0, 0).unwrap().signum(), -1.0);
+
+ // normal truncation
+ assert_eq!(
+ convert_hexf32(false, 0x0000_0000_00ff_ffff, 0),
+ Ok(16777215.0)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0x0000_0000_01ff_ffff, 0),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0xffff_ff00_0000_0000, -40),
+ Ok(16777215.0)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0xffff_ff80_0000_0000, -40),
+ Err(INEXACT)
+ );
+
+ // denormal truncation
+ assert!(convert_hexf32(false, 0x0000_0000_007f_ffff, -149).is_ok());
+ assert!(convert_hexf32(false, 0x0000_0000_00ff_ffff, -150).is_err());
+ assert!(convert_hexf32(false, 0x0000_0000_00ff_fffe, -150).is_ok());
+ assert!(convert_hexf32(false, 0xffff_ff00_0000_0000, -190).is_err());
+ assert!(convert_hexf32(false, 0xffff_fe00_0000_0000, -190).is_ok());
+
+ // minimum
+ assert!(convert_hexf32(false, 0x0000_0000_0000_0001, -149).is_ok());
+ assert!(convert_hexf32(false, 0x0000_0000_0000_0001, -150).is_err());
+ assert!(convert_hexf32(false, 0x0000_0000_0000_0002, -150).is_ok());
+ assert!(convert_hexf32(false, 0x0000_0000_0000_0002, -151).is_err());
+ assert!(convert_hexf32(false, 0x0000_0000_0000_0003, -150).is_err());
+ assert!(convert_hexf32(false, 0x0000_0000_0000_0003, -151).is_err());
+ assert!(convert_hexf32(false, 0x8000_0000_0000_0000, -212).is_ok());
+ assert!(convert_hexf32(false, 0x8000_0000_0000_0000, -213).is_err());
+
+ // maximum
+ assert_eq!(
+ convert_hexf32(false, 0x0000_0000_00ff_ffff, 104),
+ Ok(f32::MAX)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0x0000_0000_01ff_ffff, 104),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0x0000_0000_01ff_fffe, 104),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0x0000_0000_0000_0001, 128),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0x8000_0000_0000_0000, 65),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0xffff_ff00_0000_0000, 64),
+ Ok(f32::MAX)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0xffff_ff80_0000_0000, 64),
+ Err(INEXACT)
+ );
+}
+
+#[test]
+fn test_convert_hexf64() {
+ assert_eq!(convert_hexf64(false, 0, 0), Ok(0.0));
+ assert_eq!(convert_hexf64(false, 1, 0), Ok(1.0));
+ assert_eq!(convert_hexf64(false, 10, 0), Ok(10.0));
+ assert_eq!(convert_hexf64(false, 10, 1), Ok(20.0));
+ assert_eq!(convert_hexf64(false, 10, -1), Ok(5.0));
+ assert_eq!(convert_hexf64(true, 0, 0), Ok(-0.0));
+ assert_eq!(convert_hexf64(true, 1, 0), Ok(-1.0));
+
+ // negative zeroes
+ assert_eq!(convert_hexf64(false, 0, 0).unwrap().signum(), 1.0);
+ assert_eq!(convert_hexf64(true, 0, 0).unwrap().signum(), -1.0);
+
+ // normal truncation
+ assert_eq!(
+ convert_hexf64(false, 0x001f_ffff_ffff_ffff, 0),
+ Ok(9007199254740991.0)
+ );
+ assert_eq!(
+ convert_hexf64(false, 0x003f_ffff_ffff_ffff, 0),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf64(false, 0xffff_ffff_ffff_f800, -11),
+ Ok(9007199254740991.0)
+ );
+ assert_eq!(
+ convert_hexf64(false, 0xffff_ffff_ffff_fc00, -11),
+ Err(INEXACT)
+ );
+
+ // denormal truncation
+ assert!(convert_hexf64(false, 0x000f_ffff_ffff_ffff, -1074).is_ok());
+ assert!(convert_hexf64(false, 0x001f_ffff_ffff_ffff, -1075).is_err());
+ assert!(convert_hexf64(false, 0x001f_ffff_ffff_fffe, -1075).is_ok());
+ assert!(convert_hexf64(false, 0xffff_ffff_ffff_f800, -1086).is_err());
+ assert!(convert_hexf64(false, 0xffff_ffff_ffff_f000, -1086).is_ok());
+
+ // minimum
+ assert!(convert_hexf64(false, 0x0000_0000_0000_0001, -1074).is_ok());
+ assert!(convert_hexf64(false, 0x0000_0000_0000_0001, -1075).is_err());
+ assert!(convert_hexf64(false, 0x0000_0000_0000_0002, -1075).is_ok());
+ assert!(convert_hexf64(false, 0x0000_0000_0000_0002, -1076).is_err());
+ assert!(convert_hexf64(false, 0x0000_0000_0000_0003, -1075).is_err());
+ assert!(convert_hexf64(false, 0x0000_0000_0000_0003, -1076).is_err());
+ assert!(convert_hexf64(false, 0x8000_0000_0000_0000, -1137).is_ok());
+ assert!(convert_hexf64(false, 0x8000_0000_0000_0000, -1138).is_err());
+
+ // maximum
+ assert_eq!(
+ convert_hexf64(false, 0x001f_ffff_ffff_ffff, 971),
+ Ok(f64::MAX)
+ );
+ assert_eq!(
+ convert_hexf64(false, 0x003f_ffff_ffff_ffff, 971),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf64(false, 0x003f_ffff_ffff_fffe, 971),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0x0000_0000_0000_0001, 1024),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf32(false, 0x8000_0000_0000_0000, 961),
+ Err(INEXACT)
+ );
+ assert_eq!(
+ convert_hexf64(false, 0xffff_ffff_ffff_f800, 960),
+ Ok(f64::MAX)
+ );
+ assert_eq!(
+ convert_hexf64(false, 0xffff_ffff_ffff_fc00, 960),
+ Err(INEXACT)
+ );
+}
+
+/// Tries to parse a hexadecimal float literal to `f32`.
+/// The underscore is allowed only when `allow_underscore` is true.
+pub fn 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.
+pub fn 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)
+}
+
+#[test]
+fn test_parse_hexf() {
+ // issues
+ // #6 (https://github.com/lifthrasiir/hexf/issues/6)
+ assert!(parse_hexf64("0x.000000000000000000102", false).is_err());
+}