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>.
|
// <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8},
|
frac::{False, IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8},
|
||||||
sealed::SealedInt,
|
sealed::SealedInt,
|
||||||
wide_div::WideDivRem,
|
wide_div::WideDivRem,
|
||||||
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
|
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
|
||||||
|
@ -23,9 +23,35 @@ use crate::{
|
||||||
use core::{
|
use core::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
fmt::{Display, Formatter, Result as FmtResult},
|
fmt::{Display, Formatter, Result as FmtResult},
|
||||||
|
ops::{Add, Shl},
|
||||||
str::FromStr,
|
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
|
// 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
|
// Returns None for large fractions that are rounded to 1.0
|
||||||
fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option<u8> {
|
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);
|
println!("Parse error: {}", error);
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct ParseFixedError {
|
pub struct ParseFixedError {
|
||||||
kind: ParseErrorKind,
|
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 int = (0, 0);
|
||||||
let mut frac = (0, 0);
|
let mut frac = (0, 0);
|
||||||
let mut has_sign = false;
|
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 {
|
macro_rules! impl_from_str {
|
||||||
($Fixed:ident, $NBits:ident, $method:ident) => {
|
($Fixed:ident, $NBits:ident, $method:ident) => {
|
||||||
impl<Frac> FromStr for $Fixed<Frac>
|
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)
|
$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(
|
fn $all(
|
||||||
s: &str,
|
s: &str,
|
||||||
radix: i32,
|
radix: u32,
|
||||||
int_nbits: u32,
|
int_nbits: u32,
|
||||||
frac_nbits: u32,
|
frac_nbits: u32,
|
||||||
) -> Result<$Bits, ParseFixedError> {
|
) -> Result<$Bits, ParseFixedError> {
|
||||||
let Parse { neg, int, frac } = parse(s, true, 10)?;
|
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),
|
Some(frac) => (frac, false),
|
||||||
None => (0, true),
|
None => (0, true),
|
||||||
};
|
};
|
||||||
let frac = if frac_nbits == <$Bits>::NBITS {
|
if frac_nbits == <$Bits>::NBITS {
|
||||||
// special case: no int bits
|
// special case: no int bits
|
||||||
if neg {
|
let max_abs = if neg {
|
||||||
if frac > <$Bits as SealedInt>::Unsigned::MSB {
|
<$Bits as SealedInt>::Unsigned::MSB
|
||||||
err!(Overflow)
|
|
||||||
}
|
|
||||||
frac.wrapping_neg() as $Bits
|
|
||||||
} else {
|
} else {
|
||||||
if frac >= <$Bits as SealedInt>::Unsigned::MSB {
|
<$Bits as SealedInt>::Unsigned::MSB - 1
|
||||||
err!(Overflow)
|
};
|
||||||
}
|
err!(abs_frac > max_abs, Overflow);
|
||||||
frac as $Bits
|
|
||||||
}
|
}
|
||||||
|
let frac = if neg {
|
||||||
|
abs_frac.wrapping_neg() as $Bits
|
||||||
} else {
|
} else {
|
||||||
frac as $Bits
|
abs_frac as $Bits
|
||||||
};
|
};
|
||||||
let int = $int(neg, int, radix, int_nbits, whole_frac)?;
|
let int = $int(neg, int, radix, int_nbits, whole_frac)?;
|
||||||
Ok(int | frac)
|
Ok(int + frac)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn $int(
|
fn $int(
|
||||||
neg: bool,
|
neg: bool,
|
||||||
int: &str,
|
int: &str,
|
||||||
radix: i32,
|
radix: u32,
|
||||||
nbits: u32,
|
nbits: u32,
|
||||||
whole_frac: bool,
|
whole_frac: bool,
|
||||||
) -> Result<$Bits, ParseFixedError> {
|
) -> Result<$Bits, ParseFixedError> {
|
||||||
|
@ -350,7 +389,7 @@ macro_rules! impl_from_str_signed {
|
||||||
} else {
|
} else {
|
||||||
<$Bits as SealedInt>::Unsigned::MSB - 1
|
<$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) => {
|
Ok(i) => {
|
||||||
err!(i > max_abs_int, Overflow);
|
err!(i > max_abs_int, Overflow);
|
||||||
i
|
i
|
||||||
|
@ -382,7 +421,7 @@ macro_rules! impl_from_str_unsigned {
|
||||||
|
|
||||||
fn $all(
|
fn $all(
|
||||||
s: &str,
|
s: &str,
|
||||||
radix: i32,
|
radix: u32,
|
||||||
int_nbits: u32,
|
int_nbits: u32,
|
||||||
frac_nbits: u32,
|
frac_nbits: u32,
|
||||||
) -> Result<$Bits, ParseFixedError> {
|
) -> Result<$Bits, ParseFixedError> {
|
||||||
|
@ -397,7 +436,7 @@ macro_rules! impl_from_str_unsigned {
|
||||||
|
|
||||||
fn $int(
|
fn $int(
|
||||||
int: &str,
|
int: &str,
|
||||||
radix: i32,
|
radix: u32,
|
||||||
nbits: u32,
|
nbits: u32,
|
||||||
whole_frac: bool,
|
whole_frac: bool,
|
||||||
) -> Result<$Bits, ParseFixedError> {
|
) -> Result<$Bits, ParseFixedError> {
|
||||||
|
@ -412,7 +451,7 @@ macro_rules! impl_from_str_unsigned {
|
||||||
err!(whole_frac || !int.is_empty(), Overflow);
|
err!(whole_frac || !int.is_empty(), Overflow);
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
let mut acc = match int.parse::<$Bits>() {
|
let mut acc = match <$Bits>::from_str_radix(int, radix) {
|
||||||
Ok(i) => i,
|
Ok(i) => i,
|
||||||
Err(_) => err!(Overflow),
|
Err(_) => err!(Overflow),
|
||||||
};
|
};
|
||||||
|
@ -442,19 +481,25 @@ macro_rules! impl_from_str_unsigned_not128 {
|
||||||
$frac;
|
$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 {
|
if $frac_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 {
|
||||||
return $frac_half(frac, radix, nbits).map($Bits::from);
|
return $frac_half(frac, radix, nbits).map($Bits::from);
|
||||||
}
|
}
|
||||||
if frac.is_empty() {
|
if frac.is_empty() {
|
||||||
return Some(0);
|
return Some(0);
|
||||||
}
|
}
|
||||||
|
match radix {
|
||||||
|
2 => bin_str_to_bin(frac, nbits),
|
||||||
|
10 => {
|
||||||
let end = cmp::min(frac.len(), $dec_frac_digits);
|
let end = cmp::min(frac.len(), $dec_frac_digits);
|
||||||
let rem = $dec_frac_digits - end;
|
let rem = $dec_frac_digits - end;
|
||||||
let ten: $DoubleBits = 10;
|
let ten: $DoubleBits = 10;
|
||||||
let i = frac[..end].parse::<$DoubleBits>().unwrap() * ten.pow(rem as u32);
|
let i = frac[..end].parse::<$DoubleBits>().unwrap() * ten.pow(rem as u32);
|
||||||
$decode_frac(i, <$Bits as SealedInt>::NBITS - nbits)
|
$decode_frac(i, <$Bits as SealedInt>::NBITS - nbits)
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,13 +572,16 @@ impl_from_str_unsigned! {
|
||||||
get_frac128;
|
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 {
|
if nbits <= 64 {
|
||||||
return get_frac64(frac, radix, nbits).map(u128::from);
|
return get_frac64(frac, radix, nbits).map(u128::from);
|
||||||
}
|
}
|
||||||
if frac.is_empty() {
|
if frac.is_empty() {
|
||||||
return Some(0);
|
return Some(0);
|
||||||
}
|
}
|
||||||
|
match radix {
|
||||||
|
2 => bin_str_to_bin(frac, nbits),
|
||||||
|
10 => {
|
||||||
let (hi, lo) = if frac.len() <= 27 {
|
let (hi, lo) = if frac.len() <= 27 {
|
||||||
let rem = 27 - frac.len();
|
let rem = 27 - frac.len();
|
||||||
let hi = frac.parse::<u128>().unwrap() * 10u128.pow(rem as u32);
|
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);
|
let lo = frac[27..lo_end].parse::<u128>().unwrap() * 10u128.pow(rem as u32);
|
||||||
(hi, lo)
|
(hi, lo)
|
||||||
};
|
};
|
||||||
|
|
||||||
dec27_27_to_bin128(hi, lo, <u128 as SealedInt>::NBITS - nbits)
|
dec27_27_to_bin128(hi, lo, <u128 as SealedInt>::NBITS - nbits)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -229,6 +229,7 @@ mod wrapping;
|
||||||
use crate::{
|
use crate::{
|
||||||
arith::MulDivDir,
|
arith::MulDivDir,
|
||||||
frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8},
|
frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8},
|
||||||
|
from_str::FromStrRadix,
|
||||||
sealed::{Fixed, Float, Int, SealedFixed, SealedFloat, SealedInt},
|
sealed::{Fixed, Float, Int, SealedFixed, SealedFloat, SealedInt},
|
||||||
};
|
};
|
||||||
pub use crate::{from_str::ParseFixedError, wrapping::Wrapping};
|
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)
|
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 NBITS: u32 = Self::NBits::U32;
|
||||||
const IS_SIGNED: bool = Self::IsSigned::BOOL;
|
const IS_SIGNED: bool = Self::IsSigned::BOOL;
|
||||||
const MSB: Self;
|
const MSB: Self;
|
||||||
|
const ZERO: Self;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_fixed<F: Fixed>(val: F) -> Self {
|
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 all_ones_shl(shift: u32) -> Self;
|
||||||
fn is_zero(self) -> bool;
|
fn is_zero(self) -> bool;
|
||||||
fn is_negative(self) -> bool;
|
fn is_negative(self) -> bool;
|
||||||
|
fn checked_add(self, val: Self) -> Option<Self>;
|
||||||
|
|
||||||
fn to_fixed_dir_overflow(
|
fn to_fixed_dir_overflow(
|
||||||
self,
|
self,
|
||||||
|
@ -108,6 +110,7 @@ macro_rules! sealed_int {
|
||||||
type ReprFixed = $ReprFixed<U0>;
|
type ReprFixed = $ReprFixed<U0>;
|
||||||
|
|
||||||
const MSB: $Int = 1 << (Self::NBITS - 1);
|
const MSB: $Int = 1 << (Self::NBITS - 1);
|
||||||
|
const ZERO: $Int = 0;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn min_value() -> $Int {
|
fn min_value() -> $Int {
|
||||||
|
@ -154,6 +157,11 @@ macro_rules! sealed_int {
|
||||||
self == 0
|
self == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn checked_add(self, val: $Int) -> Option<$Int> {
|
||||||
|
<$Int>::checked_add(self, val)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_repr_fixed(self) -> Self::ReprFixed {
|
fn to_repr_fixed(self) -> Self::ReprFixed {
|
||||||
Self::ReprFixed::from_bits(self.int_repr())
|
Self::ReprFixed::from_bits(self.int_repr())
|
||||||
|
|
Loading…
Reference in New Issue