From 9f881db39917d8a5573cde3bcc9ff9ea109b6197 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sat, 10 Aug 2019 16:46:44 +0200 Subject: [PATCH] implement from_str_bin --- src/from_str.rs | 136 +++++++++++++++++++++++++++++------------- src/lib.rs | 1 + src/macros_from_to.rs | 28 +++++++++ src/sealed_int.rs | 8 +++ 4 files changed, 130 insertions(+), 43 deletions(-) diff --git a/src/from_str.rs b/src/from_str.rs index ba263e8..0c94fe6 100644 --- a/src/from_str.rs +++ b/src/from_str.rs @@ -14,7 +14,7 @@ // . use crate::{ - frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8}, + frac::{False, IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8}, sealed::SealedInt, wide_div::WideDivRem, FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64, @@ -23,9 +23,35 @@ use crate::{ use core::{ cmp::{self, Ordering}, fmt::{Display, Formatter, Result as FmtResult}, + ops::{Add, Shl}, str::FromStr, }; +fn bin_str_to_bin(a: &str, dump_bits: u32) -> Option +where + I: SealedInt + Shl + Add + From, +{ + debug_assert!(!a.is_empty()); + let mut digits = 8 - dump_bits; + let mut acc = I::ZERO; + let mut bytes = a.as_bytes().iter(); + while let Some(byte) = bytes.next() { + if digits == 0 { + break; + } + acc = (acc << 1) + I::from(byte - b'0'); + digits -= 1; + } + + if digits > 0 { + Some(acc << digits) + } else if let Some(byte) = bytes.next() { + acc.checked_add(I::from(byte - b'0')) + } else { + Some(acc) + } +} + // 5^3 × 2 < 2^8 => (10^3 - 1) × 2^(8-3+1) < 2^16 // Returns None for large fractions that are rounded to 1.0 fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option { @@ -171,7 +197,7 @@ let error: ParseFixedError = match s.parse::() { println!("Parse error: {}", error); ``` */ -#[derive(Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ParseFixedError { kind: ParseErrorKind, } @@ -210,7 +236,7 @@ impl Display for ParseFixedError { } } -fn parse(s: &str, can_be_neg: bool, radix: i32) -> Result, ParseFixedError> { +fn parse(s: &str, can_be_neg: bool, radix: u32) -> Result, ParseFixedError> { let mut int = (0, 0); let mut frac = (0, 0); let mut has_sign = false; @@ -272,6 +298,11 @@ fn parse(s: &str, can_be_neg: bool, radix: i32) -> Result, ParseFixedE }) } +pub(crate) trait FromStrRadix: Sized { + type Err; + fn from_str_radix(s: &str, radix: u32) -> Result; +} + macro_rules! impl_from_str { ($Fixed:ident, $NBits:ident, $method:ident) => { impl FromStr for $Fixed @@ -284,6 +315,16 @@ macro_rules! impl_from_str { $method(s, 10, Self::int_nbits(), Self::frac_nbits()).map(Self::from_bits) } } + impl FromStrRadix for $Fixed + where + Frac: Unsigned + IsLessOrEqual<$NBits, Output = True>, + { + type Err = ParseFixedError; + #[inline] + fn from_str_radix(s: &str, radix: u32) -> Result { + $method(s, radix, Self::int_nbits(), Self::frac_nbits()).map(Self::from_bits) + } + } }; } @@ -298,39 +339,37 @@ macro_rules! impl_from_str_signed { fn $all( s: &str, - radix: i32, + radix: u32, int_nbits: u32, frac_nbits: u32, ) -> Result<$Bits, ParseFixedError> { let Parse { neg, int, frac } = parse(s, true, 10)?; - let (frac, whole_frac) = match $frac(frac, radix, frac_nbits) { + let (abs_frac, whole_frac) = match $frac(frac, radix, frac_nbits) { Some(frac) => (frac, false), None => (0, true), }; - let frac = if frac_nbits == <$Bits>::NBITS { + if frac_nbits == <$Bits>::NBITS { // special case: no int bits - if neg { - if frac > <$Bits as SealedInt>::Unsigned::MSB { - err!(Overflow) - } - frac.wrapping_neg() as $Bits + let max_abs = if neg { + <$Bits as SealedInt>::Unsigned::MSB } else { - if frac >= <$Bits as SealedInt>::Unsigned::MSB { - err!(Overflow) - } - frac as $Bits - } + <$Bits as SealedInt>::Unsigned::MSB - 1 + }; + err!(abs_frac > max_abs, Overflow); + } + let frac = if neg { + abs_frac.wrapping_neg() as $Bits } else { - frac as $Bits + abs_frac as $Bits }; let int = $int(neg, int, radix, int_nbits, whole_frac)?; - Ok(int | frac) + Ok(int + frac) } fn $int( neg: bool, int: &str, - radix: i32, + radix: u32, nbits: u32, whole_frac: bool, ) -> Result<$Bits, ParseFixedError> { @@ -350,7 +389,7 @@ macro_rules! impl_from_str_signed { } else { <$Bits as SealedInt>::Unsigned::MSB - 1 }; - let mut acc = match int.parse::<<$Bits as SealedInt>::Unsigned>() { + let mut acc = match <$Bits as SealedInt>::Unsigned::from_str_radix(int, radix) { Ok(i) => { err!(i > max_abs_int, Overflow); i @@ -382,7 +421,7 @@ macro_rules! impl_from_str_unsigned { fn $all( s: &str, - radix: i32, + radix: u32, int_nbits: u32, frac_nbits: u32, ) -> Result<$Bits, ParseFixedError> { @@ -397,7 +436,7 @@ macro_rules! impl_from_str_unsigned { fn $int( int: &str, - radix: i32, + radix: u32, nbits: u32, whole_frac: bool, ) -> Result<$Bits, ParseFixedError> { @@ -412,7 +451,7 @@ macro_rules! impl_from_str_unsigned { err!(whole_frac || !int.is_empty(), Overflow); return Ok(0); } - let mut acc = match int.parse::<$Bits>() { + let mut acc = match <$Bits>::from_str_radix(int, radix) { Ok(i) => i, Err(_) => err!(Overflow), }; @@ -442,18 +481,24 @@ macro_rules! impl_from_str_unsigned_not128 { $frac; } - fn $frac(frac: &str, radix: i32, nbits: u32) -> Option<$Bits> { + fn $frac(frac: &str, radix: u32, nbits: u32) -> Option<$Bits> { if $frac_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 { return $frac_half(frac, radix, nbits).map($Bits::from); } if frac.is_empty() { return Some(0); } - let end = cmp::min(frac.len(), $dec_frac_digits); - let rem = $dec_frac_digits - end; - let ten: $DoubleBits = 10; - let i = frac[..end].parse::<$DoubleBits>().unwrap() * ten.pow(rem as u32); - $decode_frac(i, <$Bits as SealedInt>::NBITS - nbits) + match radix { + 2 => bin_str_to_bin(frac, nbits), + 10 => { + let end = cmp::min(frac.len(), $dec_frac_digits); + let rem = $dec_frac_digits - end; + let ten: $DoubleBits = 10; + let i = frac[..end].parse::<$DoubleBits>().unwrap() * ten.pow(rem as u32); + $decode_frac(i, <$Bits as SealedInt>::NBITS - nbits) + } + _ => unreachable!(), + } } }; } @@ -527,26 +572,31 @@ impl_from_str_unsigned! { get_frac128; } -fn get_frac128(frac: &str, radix: i32, nbits: u32) -> Option { +fn get_frac128(frac: &str, radix: u32, nbits: u32) -> Option { if nbits <= 64 { return get_frac64(frac, radix, nbits).map(u128::from); } if frac.is_empty() { return Some(0); } - let (hi, lo) = if frac.len() <= 27 { - let rem = 27 - frac.len(); - let hi = frac.parse::().unwrap() * 10u128.pow(rem as u32); - (hi, 0) - } else { - let hi = frac[..27].parse::().unwrap(); - let lo_end = cmp::min(frac.len(), 54); - let rem = 54 - lo_end; - let lo = frac[27..lo_end].parse::().unwrap() * 10u128.pow(rem as u32); - (hi, lo) - }; - - dec27_27_to_bin128(hi, lo, ::NBITS - nbits) + match radix { + 2 => bin_str_to_bin(frac, nbits), + 10 => { + let (hi, lo) = if frac.len() <= 27 { + let rem = 27 - frac.len(); + let hi = frac.parse::().unwrap() * 10u128.pow(rem as u32); + (hi, 0) + } else { + let hi = frac[..27].parse::().unwrap(); + let lo_end = cmp::min(frac.len(), 54); + let rem = 54 - lo_end; + let lo = frac[27..lo_end].parse::().unwrap() * 10u128.pow(rem as u32); + (hi, lo) + }; + dec27_27_to_bin128(hi, lo, ::NBITS - nbits) + } + _ => unreachable!(), + } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 853119e..3ec4bc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -229,6 +229,7 @@ mod wrapping; use crate::{ arith::MulDivDir, frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8}, + from_str::FromStrRadix, sealed::{Fixed, Float, Int, SealedFixed, SealedFloat, SealedInt}, }; pub use crate::{from_str::ParseFixedError, wrapping::Wrapping}; diff --git a/src/macros_from_to.rs b/src/macros_from_to.rs index 6886d9e..75356d6 100644 --- a/src/macros_from_to.rs +++ b/src/macros_from_to.rs @@ -1225,5 +1225,33 @@ assert_eq!(Fix::overflowing_from_float(large), (wrapped, true)); SealedFloat::overflowing_to_fixed(val) } ); + + comment!( + "Converts a string slice in the binary radix. + +# Examples + +```rust +type Fix = fixed::", + $s_fixed, + "; +// 1.75 is 1.11 in binary +let f = Fix::from_str_bin(\"01.1100\"); +let check = Fix::from_bits(0b111 << (4 - 2)); +assert_eq!(f, Ok(check)); +", + if_signed_else_empty_str!( + $Signedness, + "let neg = Fix::from_str_bin(\"-1.11\"); +assert_eq!(neg, Ok(-check)); +", + ), + "``` +"; + #[inline] + pub fn from_str_bin(src: &str) -> Result<$Fixed, ParseFixedError> { + FromStrRadix::from_str_radix(src, 2) + } + ); }; } diff --git a/src/sealed_int.rs b/src/sealed_int.rs index 5bacdb0..c35e2f5 100644 --- a/src/sealed_int.rs +++ b/src/sealed_int.rs @@ -33,6 +33,7 @@ pub trait SealedInt: Copy + Ord + Debug + Display { const NBITS: u32 = Self::NBits::U32; const IS_SIGNED: bool = Self::IsSigned::BOOL; const MSB: Self; + const ZERO: Self; #[inline] fn from_fixed(val: F) -> Self { @@ -85,6 +86,7 @@ pub trait SealedInt: Copy + Ord + Debug + Display { fn all_ones_shl(shift: u32) -> Self; fn is_zero(self) -> bool; fn is_negative(self) -> bool; + fn checked_add(self, val: Self) -> Option; fn to_fixed_dir_overflow( self, @@ -108,6 +110,7 @@ macro_rules! sealed_int { type ReprFixed = $ReprFixed; const MSB: $Int = 1 << (Self::NBITS - 1); + const ZERO: $Int = 0; #[inline] fn min_value() -> $Int { @@ -154,6 +157,11 @@ macro_rules! sealed_int { self == 0 } + #[inline] + fn checked_add(self, val: $Int) -> Option<$Int> { + <$Int>::checked_add(self, val) + } + #[inline] fn to_repr_fixed(self) -> Self::ReprFixed { Self::ReprFixed::from_bits(self.int_repr())