From 4187d84e4174de0b7d61c7ad6875af8613c8ebb0 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sat, 10 Aug 2019 18:55:27 +0200 Subject: [PATCH] add octal and hex --- src/from_str.rs | 73 ++++++++++++++++++++++++++++++++++--------- src/macros_from_to.rs | 60 +++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/from_str.rs b/src/from_str.rs index b6d76a7..c0b9960 100644 --- a/src/from_str.rs +++ b/src/from_str.rs @@ -32,24 +32,63 @@ where I: SealedInt + Shl + Add + From, { debug_assert!(!a.is_empty()); - let mut digits = 8 - dump_bits; + let mut bits = I::NBITS - dump_bits; let mut acc = I::ZERO; - let mut bytes = a.as_bytes().iter(); - while let Some(byte) = bytes.next() { - if digits == 0 { - break; + for byte in a.as_bytes() { + let val = byte - b'0'; + if bits < 1 { + // round + return acc.checked_add(I::from(val)); } - acc = (acc << 1) + I::from(byte - b'0'); - digits -= 1; + acc = (acc << 1) + I::from(val); + bits -= 1; } + Some(acc << bits) +} - if digits > 0 { - Some(acc << digits) - } else if let Some(byte) = bytes.next() { - acc.checked_add(I::from(byte - b'0')) - } else { - Some(acc) +fn oct_str_to_bin(a: &str, dump_bits: u32) -> Option +where + I: SealedInt + Shl + Add + From, +{ + debug_assert!(!a.is_empty()); + let mut bits = I::NBITS - dump_bits; + let mut acc = I::ZERO; + for byte in a.as_bytes() { + let val = byte - b'0'; + if bits < 3 { + acc = (acc << bits) + I::from(val >> (3 - bits)); + // round + return acc.checked_add(I::from((val >> (2 - bits)) & 1)); + } + acc = (acc << 3) + I::from(val); + bits -= 3; } + Some(acc << bits) +} + +fn hex_str_to_bin(a: &str, dump_bits: u32) -> Option +where + I: SealedInt + Shl + Add + From, +{ + debug_assert!(!a.is_empty()); + let mut bits = I::NBITS - dump_bits; + let mut acc = I::ZERO; + for byte in a.as_bytes() { + let val = match byte { + b @ b'0'..=b'9' => b - b'0', + b @ b'A'..=b'F' => b - b'A' + 10, + b @ b'a'..=b'f' => b - b'a' + 10, + _ => 0, + }; + if bits < 4 { + acc = (acc << bits) + I::from(val >> (4 - bits)); + // round + return acc.checked_add(I::from((val >> (3 - bits)) & 1)); + } + acc = (acc << 4) + I::from(val); + bits -= 4; + } + Some(acc << bits) } // 5^3 × 2 < 2^8 => (10^3 - 1) × 2^(8-3+1) < 2^16 @@ -343,7 +382,7 @@ macro_rules! impl_from_str_signed { int_nbits: u32, frac_nbits: u32, ) -> Result<$Bits, ParseFixedError> { - let Parse { neg, int, frac } = parse(s, true, 10)?; + let Parse { neg, int, frac } = parse(s, true, radix)?; let (abs_frac, whole_frac) = match $frac(frac, radix, frac_nbits) { Some(frac) => (frac, false), None => (0, true), @@ -381,7 +420,7 @@ macro_rules! impl_from_str_unsigned { int_nbits: u32, frac_nbits: u32, ) -> Result<$Bits, ParseFixedError> { - let Parse { int, frac, .. } = parse(s, false, 10)?; + let Parse { int, frac, .. } = parse(s, false, radix)?; let (frac, whole_frac) = match $frac(frac, radix, frac_nbits) { Some(frac) => (frac, false), None => (0, true), @@ -449,6 +488,8 @@ macro_rules! impl_from_str_unsigned_not128 { } match radix { 2 => bin_str_to_bin(frac, nbits), + 8 => oct_str_to_bin(frac, nbits), + 16 => hex_str_to_bin(frac, nbits), 10 => { let end = cmp::min(frac.len(), $dec_frac_digits); let rem = $dec_frac_digits - end; @@ -694,6 +735,8 @@ mod tests { assert_eq!((neg, int, frac), (false, "", "34")); let Parse { neg, int, frac } = parse("0", false, 10).unwrap(); assert_eq!((neg, int, frac), (false, "0", "")); + let Parse { neg, int, frac } = parse("-.C1A0", true, 16).unwrap(); + assert_eq!((neg, int, frac), (true, "", "C1A0")); let ParseFixedError { kind } = parse("0 ", true, 10).unwrap_err(); assert_eq!(kind, ParseErrorKind::InvalidDigit); diff --git a/src/macros_from_to.rs b/src/macros_from_to.rs index 75356d6..c53eb3f 100644 --- a/src/macros_from_to.rs +++ b/src/macros_from_to.rs @@ -1227,7 +1227,7 @@ assert_eq!(Fix::overflowing_from_float(large), (wrapped, true)); ); comment!( - "Converts a string slice in the binary radix. + "Converts a string slice containing binary digits to a fixed-point number. # Examples @@ -1236,7 +1236,7 @@ type Fix = fixed::", $s_fixed, "; // 1.75 is 1.11 in binary -let f = Fix::from_str_bin(\"01.1100\"); +let f = Fix::from_str_bin(\"1.11\"); let check = Fix::from_bits(0b111 << (4 - 2)); assert_eq!(f, Ok(check)); ", @@ -1253,5 +1253,61 @@ assert_eq!(neg, Ok(-check)); FromStrRadix::from_str_radix(src, 2) } ); + + comment!( + "Converts a string slice containing octal digits to a fixed-point number. + +# Examples + +```rust +type Fix = fixed::", + $s_fixed, + "; +// 1.75 is 1.11 in binary, 1.6 in octal +let f = Fix::from_str_octal(\"1.6\"); +let check = Fix::from_bits(0b111 << (4 - 2)); +assert_eq!(f, Ok(check)); +", + if_signed_else_empty_str!( + $Signedness, + "let neg = Fix::from_str_octal(\"-1.6\"); +assert_eq!(neg, Ok(-check)); +", + ), + "``` +"; + #[inline] + pub fn from_str_octal(src: &str) -> Result<$Fixed, ParseFixedError> { + FromStrRadix::from_str_radix(src, 8) + } + ); + + comment!( + "Converts a string slice containing hexadecimal digits to a fixed-point number. + +# Examples + +```rust +type Fix = fixed::", + $s_fixed, + "; +// 1.75 is 1.11 in binary, 1.C in hexadecimal +let f = Fix::from_str_hex(\"1.C\"); +let check = Fix::from_bits(0b111 << (4 - 2)); +assert_eq!(f, Ok(check)); +", + if_signed_else_empty_str!( + $Signedness, + "let neg = Fix::from_str_hex(\"-1.C\"); +assert_eq!(neg, Ok(-check)); +", + ), + "``` +"; + #[inline] + pub fn from_str_hex(src: &str) -> Result<$Fixed, ParseFixedError> { + FromStrRadix::from_str_radix(src, 16) + } + ); }; }