implement from_str_bin
This commit is contained in:
parent
852040e876
commit
9f881db399
100
src/from_str.rs
100
src/from_str.rs
|
@ -14,7 +14,7 @@
|
|||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
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<I>(a: &str, dump_bits: u32) -> Option<I>
|
||||
where
|
||||
I: SealedInt<IsSigned = False> + Shl<u32, Output = I> + Add<Output = I> + From<u8>,
|
||||
{
|
||||
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<u8> {
|
||||
|
@ -171,7 +197,7 @@ let error: ParseFixedError = match s.parse::<I16F16>() {
|
|||
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<Parse<'_>, ParseFixedError> {
|
||||
fn parse(s: &str, can_be_neg: bool, radix: u32) -> Result<Parse<'_>, 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<Parse<'_>, ParseFixedE
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) trait FromStrRadix: Sized {
|
||||
type Err;
|
||||
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err>;
|
||||
}
|
||||
|
||||
macro_rules! impl_from_str {
|
||||
($Fixed:ident, $NBits:ident, $method:ident) => {
|
||||
impl<Frac> FromStr for $Fixed<Frac>
|
||||
|
@ -284,6 +315,16 @@ macro_rules! impl_from_str {
|
|||
$method(s, 10, Self::int_nbits(), Self::frac_nbits()).map(Self::from_bits)
|
||||
}
|
||||
}
|
||||
impl<Frac> FromStrRadix for $Fixed<Frac>
|
||||
where
|
||||
Frac: Unsigned + IsLessOrEqual<$NBits, Output = True>,
|
||||
{
|
||||
type Err = ParseFixedError;
|
||||
#[inline]
|
||||
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err> {
|
||||
$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,19 +481,25 @@ 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);
|
||||
}
|
||||
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,13 +572,16 @@ impl_from_str_unsigned! {
|
|||
get_frac128;
|
||||
}
|
||||
|
||||
fn get_frac128(frac: &str, radix: i32, nbits: u32) -> Option<u128> {
|
||||
fn get_frac128(frac: &str, radix: u32, nbits: u32) -> Option<u128> {
|
||||
if nbits <= 64 {
|
||||
return get_frac64(frac, radix, nbits).map(u128::from);
|
||||
}
|
||||
if frac.is_empty() {
|
||||
return Some(0);
|
||||
}
|
||||
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::<u128>().unwrap() * 10u128.pow(rem as u32);
|
||||
|
@ -545,8 +593,10 @@ fn get_frac128(frac: &str, radix: i32, nbits: u32) -> Option<u128> {
|
|||
let lo = frac[27..lo_end].parse::<u128>().unwrap() * 10u128.pow(rem as u32);
|
||||
(hi, lo)
|
||||
};
|
||||
|
||||
dec27_27_to_bin128(hi, lo, <u128 as SealedInt>::NBITS - nbits)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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,
|
||||
"<fixed::frac::U4>;
|
||||
// 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<Frac>, ParseFixedError> {
|
||||
FromStrRadix::from_str_radix(src, 2)
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<F: 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<Self>;
|
||||
|
||||
fn to_fixed_dir_overflow(
|
||||
self,
|
||||
|
@ -108,6 +110,7 @@ macro_rules! sealed_int {
|
|||
type ReprFixed = $ReprFixed<U0>;
|
||||
|
||||
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())
|
||||
|
|
Loading…
Reference in New Issue