very long decimal fraction strings are now parsed correctly

This commit is contained in:
Trevor Spiteri 2019-08-13 19:01:01 +02:00
parent 83ef24b241
commit e740a88547
6 changed files with 347 additions and 96 deletions

View File

@ -49,6 +49,13 @@ Various conversion methods are available:
## Whats new
### Version 0.4.2 news (unreleased)
* Bug fix: parsing of decimal fractions was fixed to give correctly
rounded results for long decimal fraction strings, for example
with four fractional bits, 0.96874999… (just below 3132) and
0.96875 (3132) are now parsed correctly as 0.9375 (1516) and 1.0.
### Version 0.4.1 news (2019-08-12)
* All fixed-point types now implement [`FromStr`].

View File

@ -5,6 +5,14 @@ modification, are permitted in any medium without royalty provided the
copyright notice and this notice are preserved. This file is offered
as-is, without any warranty. -->
Version 0.4.2 (unreleased)
==========================
* Bug fix: parsing of decimal fractions was fixed to give correctly
rounded results for long decimal fraction strings, for example
with four fractional bits, 0.96874999… (just below 3132) and
0.96875 (3132) are now parsed correctly as 0.9375 (1516) and 1.0.
Version 0.4.1 (2019-08-12)
==========================

View File

@ -228,17 +228,18 @@ fn dec_frac_digits(frac_bits: u32) -> u32 {
(frac_bits * 3 + i) / 10
}
trait Mul10: Sized {
fn mul10(self) -> (Self, u8);
pub(crate) trait Mul10: Sized {
fn mul10_assign(&mut self) -> u8;
}
macro_rules! mul10_widen {
($Single:ty, $Double:ty) => {
impl Mul10 for $Single {
#[inline]
fn mul10(self) -> ($Single, u8) {
fn mul10_assign(&mut self) -> u8 {
const NBITS: usize = 8 * mem::size_of::<$Single>();
let prod = <$Double>::from(self) * 10;
(prod as $Single, (prod >> NBITS) as u8)
let prod = <$Double>::from(*self) * 10;
*self = prod as $Single;
(prod >> NBITS) as u8
}
}
};
@ -249,20 +250,18 @@ mul10_widen! { u32, u64 }
mul10_widen! { u64, u128 }
impl Mul10 for u128 {
#[inline]
fn mul10(self) -> (u128, u8) {
fn mul10_assign(&mut self) -> u8 {
const LO_MASK: u128 = !(!0 << 64);
let hi = (self >> 64) * 10;
let lo = (self & LO_MASK) * 10;
let hi = (*self >> 64) * 10;
let lo = (*self & LO_MASK) * 10;
// Workaround for https://github.com/rust-lang/rust/issues/63384
// let (wrapped, overflow) = (hi << 64).overflowing_add(lo);
// ((hi >> 64) as u8 + u8::from(overflow), wrapped)
let (hi_lo, hi_hi) = (hi as u64, (hi >> 64) as u64);
let (lo_lo, lo_hi) = (lo as u64, (lo >> 64) as u64);
let (wrapped, overflow) = hi_lo.overflowing_add(lo_hi);
(
(u128::from(wrapped) << 64) | u128::from(lo_lo),
hi_hi as u8 + u8::from(overflow),
)
*self = (u128::from(wrapped) << 64) | u128::from(lo_lo);
hi_hi as u8 + u8::from(overflow)
}
}
@ -283,9 +282,7 @@ macro_rules! fmt_dec_helper {
#[inline]
fn take_frac_digit(&mut self) -> u8 {
let (next, ret) = self.mul10();
*self = next;
ret
self.mul10_assign()
}
}
)* };

View File

@ -14,6 +14,7 @@
// <https://opensource.org/licenses/MIT>.
use crate::{
display::Mul10,
frac::False,
sealed::SealedInt,
types::{LeEqU128, LeEqU16, LeEqU32, LeEqU64, LeEqU8},
@ -22,9 +23,9 @@ use crate::{
FixedU8,
};
use core::{
cmp::{self, Ordering},
cmp::Ordering,
fmt::{Display, Formatter, Result as FmtResult},
ops::{Add, Shl, Shr},
ops::{Add, Mul, Shl, Shr},
str::FromStr,
};
@ -115,7 +116,6 @@ where
Some(acc << rem_bits)
}
#[inline]
fn unchecked_hex_digit(byte: u8) -> u8 {
// We know that byte is a valid hex:
// * b'0'..=b'9' (0x30..=0x39) => byte & 0x0f
@ -168,14 +168,131 @@ where
Some(acc << rem_bits)
}
enum Round {
Nearest,
Floor,
}
trait DecToBin: Sized {
type Double;
fn parse_is_short(s: &str) -> (Self::Double, bool);
fn dec_to_bin(val: Self::Double, nbits: u32, round: Round) -> Option<Self>;
}
macro_rules! impl_dec_to_bin {
($Single:ident, $Double:ident, $dec:expr, $method:ident) => {
impl DecToBin for $Single {
type Double = $Double;
fn parse_is_short(s: &str) -> ($Double, bool) {
let (is_short, slice, pad) = if s.len() <= $dec {
let ten: $Double = 10;
(true, s, ten.pow($dec - s.len() as u32))
} else {
(false, &s[..$dec], 1)
};
let val = slice.parse::<$Double>().unwrap() * pad;
(val, is_short)
}
fn dec_to_bin(val: $Double, nbits: u32, round: Round) -> Option<$Single> {
$method(val, nbits, round)
}
}
};
}
impl_dec_to_bin! { u8, u16, 3, dec3_to_bin8 }
impl_dec_to_bin! { u16, u32, 6, dec6_to_bin16 }
impl_dec_to_bin! { u32, u64, 13, dec13_to_bin32 }
impl_dec_to_bin! { u64, u128, 27, dec27_to_bin64 }
impl DecToBin for u128 {
type Double = (u128, u128);
fn parse_is_short(s: &str) -> (Self::Double, bool) {
if s.len() <= 27 {
let rem = 27 - s.len();
let hi = s.parse::<u128>().unwrap() * 10u128.pow(rem as u32);
((hi, 0), true)
} else {
let hi = s[..27].parse::<u128>().unwrap();
let (is_short, slice, pad) = if s.len() <= 54 {
(true, &s[27..], 10u128.pow(54 - s.len() as u32))
} else {
(false, &s[27..54], 1)
};
let lo = slice.parse::<u128>().unwrap() * pad;
((hi, lo), is_short)
}
}
fn dec_to_bin(val: Self::Double, nbits: u32, round: Round) -> Option<Self> {
dec27_27_to_bin128(val, nbits, round)
}
}
fn dec_str_frac_to_bin<I>(s: &str, nbits: u32) -> Option<I>
where
I: SealedInt<IsSigned = False> + FromStr + From<u8> + DecToBin,
I: Mul10 + Shl<u32, Output = I> + Shr<u32, Output = I> + Add<Output = I> + Mul<Output = I>,
{
let (val, is_short) = I::parse_is_short(s);
let one = I::from(1u8);
let dump_bits = I::NBITS - nbits;
// if is_short, dec_to_bin can round and give correct answer immediately
let round = if is_short {
Round::Nearest
} else {
Round::Floor
};
let floor = I::dec_to_bin(val, nbits, round)?;
if is_short {
return Some(floor);
}
// since !is_short, we have a floor and we have to check whether we need to increment
// add_5 is to add rounding when all bits are used
let (mut boundary, mut add_5) = if nbits == 0 {
(one << (I::NBITS - 1), false)
} else if dump_bits == 0 {
(floor, true)
} else {
((floor << dump_bits) + (one << (dump_bits - 1)), false)
};
for &byte in s.as_bytes() {
let mut boundary_digit = boundary.mul10_assign();
if add_5 {
let (wrapped, overflow) = boundary.overflowing_add(I::from(5u8));
boundary = wrapped;
if overflow {
boundary_digit += 1;
}
add_5 = false;
}
if byte - b'0' < boundary_digit {
return Some(floor);
}
if byte - b'0' > boundary_digit {
break;
}
}
// ≥ boundary, so we round up
let next_up = floor.checked_add(one)?;
if dump_bits != 0 && (next_up >> nbits).traits().ne(&I::ZERO.traits()) {
None
} else {
Some(next_up)
}
}
// 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(val: u16, nbits: u32) -> Option<u8> {
fn dec3_to_bin8(val: u16, nbits: u32, round: Round) -> Option<u8> {
debug_assert!(val < 10u16.pow(3));
let dump_bits = 8 - nbits;
let divisor = 5u16.pow(3) * 2;
let shift = val << (8 - 3 + 1) >> dump_bits;
let round = shift + (divisor / 2);
let round = match round {
Round::Nearest => shift + (divisor / 2),
Round::Floor => shift,
};
if round >> nbits >= divisor {
None
} else {
@ -184,12 +301,15 @@ fn dec3_to_bin8(val: u16, nbits: u32) -> Option<u8> {
}
// 5^6 × 2 < 2^16 => (10^6 - 1) × 2^(16-6+1) < 2^32
// Returns None for large fractions that are rounded to 1.0
fn dec6_to_bin16(val: u32, nbits: u32) -> Option<u16> {
fn dec6_to_bin16(val: u32, nbits: u32, round: Round) -> Option<u16> {
debug_assert!(val < 10u32.pow(6));
let dump_bits = 16 - nbits;
let divisor = 5u32.pow(6) * 2;
let shift = val << (16 - 6 + 1) >> dump_bits;
let round = shift + (divisor / 2);
let round = match round {
Round::Nearest => shift + (divisor / 2),
Round::Floor => shift,
};
if round >> nbits >= divisor {
None
} else {
@ -198,12 +318,15 @@ fn dec6_to_bin16(val: u32, nbits: u32) -> Option<u16> {
}
// 5^13 × 2 < 2^32 => (10^13 - 1) × 2^(32-13+1) < 2^64
// Returns None for large fractions that are rounded to 1.0
fn dec13_to_bin32(val: u64, nbits: u32) -> Option<u32> {
fn dec13_to_bin32(val: u64, nbits: u32, round: Round) -> Option<u32> {
debug_assert!(val < 10u64.pow(13));
let dump_bits = 32 - nbits;
let divisor = 5u64.pow(13) * 2;
let shift = val << (32 - 13 + 1) >> dump_bits;
let round = shift + (divisor / 2);
let round = match round {
Round::Nearest => shift + (divisor / 2),
Round::Floor => shift,
};
if round >> nbits >= divisor {
None
} else {
@ -212,12 +335,15 @@ fn dec13_to_bin32(val: u64, nbits: u32) -> Option<u32> {
}
// 5^27 × 2 < 2^64 => (10^27 - 1) × 2^(64-27+1) < 2^128
// Returns None for large fractions that are rounded to 1.0
fn dec27_to_bin64(val: u128, nbits: u32) -> Option<u64> {
fn dec27_to_bin64(val: u128, nbits: u32, round: Round) -> Option<u64> {
debug_assert!(val < 10u128.pow(27));
let dump_bits = 64 - nbits;
let divisor = 5u128.pow(27) * 2;
let shift = val << (64 - 27 + 1) >> dump_bits;
let round = shift + (divisor / 2);
let round = match round {
Round::Nearest => shift + (divisor / 2),
Round::Floor => shift,
};
if round >> nbits >= divisor {
None
} else {
@ -226,7 +352,7 @@ fn dec27_to_bin64(val: u128, nbits: u32) -> Option<u64> {
}
// 5^54 × 2 < 2^128 => (10^54 - 1) × 2^(128-54+1) < 2^256
// Returns None for large fractions that are rounded to 1.0
fn dec27_27_to_bin128(hi: u128, lo: u128, nbits: u32) -> Option<u128> {
fn dec27_27_to_bin128((hi, lo): (u128, u128), nbits: u32, round: Round) -> Option<u128> {
debug_assert!(hi < 10u128.pow(27));
debug_assert!(lo < 10u128.pow(27));
let dump_bits = 128 - nbits;
@ -253,7 +379,10 @@ fn dec27_27_to_bin128(hi: u128, lo: u128, nbits: u32) -> Option<u128> {
shift_hi = comb_hi;
}
};
let (round_lo, overflow) = shift_lo.overflowing_add(divisor / 2);
let (round_lo, overflow) = match round {
Round::Nearest => shift_lo.overflowing_add(divisor / 2),
Round::Floor => (shift_lo, false),
};
let round_hi = if overflow { shift_hi + 1 } else { shift_hi };
let whole_compare = if dump_bits == 0 {
round_hi
@ -481,7 +610,6 @@ macro_rules! impl_from_str_unsigned {
fn $all:ident;
fn $int:ident, ($int_half:ident, $int_half_cond:expr);
fn $frac:ident, ($frac_half:ident, $frac_half_cond:expr);
$frac_dec:ident;
) => {
impl_from_str! { $Fixed, $LeEqU, $all }
@ -505,9 +633,14 @@ macro_rules! impl_from_str_unsigned {
if $int_half_cond && nbits <= HALF {
return $int_half(int, radix, nbits, whole_frac).map(|x| $Bits::from(x) << HALF);
}
if int.is_empty() && !whole_frac {
return Some(0);
} else if int.is_empty() || nbits == 0 {
} else if int.is_empty() && nbits != 0 {
return Some(1);
} else if int.is_empty() {
return None;
} else if nbits == 0 {
return None;
}
let mut parsed_int = match radix {
@ -539,53 +672,24 @@ macro_rules! impl_from_str_unsigned {
2 => bin_str_frac_to_bin(frac, nbits),
8 => oct_str_frac_to_bin(frac, nbits),
16 => hex_str_frac_to_bin(frac, nbits),
10 => $frac_dec(frac, nbits),
10 => dec_str_frac_to_bin(frac, nbits),
_ => unreachable!(),
}
}
};
}
macro_rules! impl_from_str_unsigned_not128 {
(
$Fixed:ident, $LeEqU:ident, $Bits:ident;
fn $all:ident;
fn $int:ident, ($int_half:ident, $int_half_cond:expr);
fn $frac:ident, ($frac_half:ident, $frac_half_cond:expr);
fn $frac_dec:ident;
$decode_frac:ident, $dec_frac_digits:expr, $DoubleBits:ident;
) => {
impl_from_str_unsigned! {
$Fixed, $LeEqU, $Bits;
fn $all;
fn $int, ($int_half, $int_half_cond);
fn $frac, ($frac_half, $frac_half_cond);
$frac_dec;
}
fn $frac_dec(frac: &str, nbits: u32) -> Option<$Bits> {
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, nbits)
}
};
}
impl_from_str_signed! {
FixedI8, LeEqU8, i8;
fn from_str_i8;
get_int8;
get_frac8;
}
impl_from_str_unsigned_not128! {
impl_from_str_unsigned! {
FixedU8, LeEqU8, u8;
fn from_str_u8;
fn get_int8, (get_int8, false);
fn get_frac8, (get_frac8, false);
fn get_frac8_dec;
dec3_to_bin8, 3, u16;
}
impl_from_str_signed! {
@ -594,13 +698,11 @@ impl_from_str_signed! {
get_int16;
get_frac16;
}
impl_from_str_unsigned_not128! {
impl_from_str_unsigned! {
FixedU16, LeEqU16, u16;
fn from_str_u16;
fn get_int16, (get_int8, true);
fn get_frac16, (get_frac8, true);
fn get_frac16_dec;
dec6_to_bin16, 6, u32;
}
impl_from_str_signed! {
@ -609,13 +711,11 @@ impl_from_str_signed! {
get_int32;
get_frac32;
}
impl_from_str_unsigned_not128! {
impl_from_str_unsigned! {
FixedU32, LeEqU32, u32;
fn from_str_u32;
fn get_int32, (get_int16, true);
fn get_frac32, (get_frac16, true);
fn get_frac32_dec;
dec13_to_bin32, 13, u64;
}
impl_from_str_signed! {
@ -624,13 +724,11 @@ impl_from_str_signed! {
get_int64;
get_frac64;
}
impl_from_str_unsigned_not128! {
impl_from_str_unsigned! {
FixedU64, LeEqU64, u64;
fn from_str_u64;
fn get_int64, (get_int32, true);
fn get_frac64, (get_frac32, true);
fn get_frac64_dec;
dec27_to_bin64, 27, u128;
}
impl_from_str_signed! {
@ -644,22 +742,6 @@ impl_from_str_unsigned! {
fn from_str_u128;
fn get_int128, (get_int64, true);
fn get_frac128, (get_frac64, true);
get_frac128_dec;
}
fn get_frac128_dec(frac: &str, nbits: u32) -> Option<u128> {
let (hi, lo) = if frac.len() <= 27 {
let rem = 27 - frac.len();
let hi = frac.parse::<u128>().unwrap() * 10u128.pow(rem as u32);
(hi, 0)
} else {
let hi = frac[..27].parse::<u128>().unwrap();
let lo_end = cmp::min(frac.len(), 54);
let rem = 54 - lo_end;
let lo = frac[27..lo_end].parse::<u128>().unwrap() * 10u128.pow(rem as u32);
(hi, lo)
};
dec27_27_to_bin128(hi, lo, nbits)
}
#[cfg(test)]
@ -672,7 +754,7 @@ mod tests {
let two_pow = 8f64.exp2();
let limit = 1000;
for i in 0..limit {
let ans = dec3_to_bin8(i, 8);
let ans = dec3_to_bin8(i, 8, Round::Nearest);
let approx = two_pow * f64::from(i) / f64::from(limit);
let error = (ans.map(f64::from).unwrap_or(two_pow) - approx).abs();
assert!(
@ -691,7 +773,7 @@ mod tests {
let two_pow = 16f64.exp2();
let limit = 1_000_000;
for i in 0..limit {
let ans = dec6_to_bin16(i, 16);
let ans = dec6_to_bin16(i, 16, Round::Nearest);
let approx = two_pow * f64::from(i) / f64::from(limit);
let error = (ans.map(f64::from).unwrap_or(two_pow) - approx).abs();
assert!(
@ -720,7 +802,7 @@ mod tests {
limit / 2 + iter,
limit - iter - 1,
] {
let ans = dec13_to_bin32(i, 32);
let ans = dec13_to_bin32(i, 32, Round::Nearest);
let approx = two_pow * i as f64 / limit as f64;
let error = (ans.map(f64::from).unwrap_or(two_pow) - approx).abs();
assert!(
@ -750,7 +832,7 @@ mod tests {
limit / 2 + iter,
limit - iter - 1,
] {
let ans = dec27_to_bin64(i, 64);
let ans = dec27_to_bin64(i, 64, Round::Nearest);
let approx = two_pow * i as f64 / limit as f64;
let error = (ans.map(|x| x as f64).unwrap_or(two_pow) - approx).abs();
assert!(
@ -769,21 +851,24 @@ mod tests {
fn check_dec27_27() {
let nines = 10u128.pow(27) - 1;
let zeros = 0;
let too_big = dec27_27_to_bin128(nines, nines, 128);
let too_big = dec27_27_to_bin128((nines, nines), 128, Round::Nearest);
assert_eq!(too_big, None);
let big = dec27_27_to_bin128(nines, zeros, 128);
let big = dec27_27_to_bin128((nines, zeros), 128, Round::Nearest);
assert_eq!(
big,
Some(340_282_366_920_938_463_463_374_607_091_485_844_535)
);
let small = dec27_27_to_bin128(zeros, nines, 128);
let small = dec27_27_to_bin128((zeros, nines), 128, Round::Nearest);
assert_eq!(small, Some(340_282_366_921));
let zero = dec27_27_to_bin128(zeros, zeros, 128);
let zero = dec27_27_to_bin128((zeros, zeros), 128, Round::Nearest);
assert_eq!(zero, Some(0));
let x = dec27_27_to_bin128(
123_456_789_012_345_678_901_234_567,
987_654_321_098_765_432_109_876_543,
(
123_456_789_012_345_678_901_234_567,
987_654_321_098_765_432_109_876_543,
),
128,
Round::Nearest,
);
assert_eq!(x, Some(42_010_168_377_579_896_403_540_037_811_203_677_112));
}
@ -1385,4 +1470,149 @@ mod tests {
ParseErrorKind::Overflow,
);
}
use std::{format, string::String};
struct Fractions {
zero: String,
eps: String,
one_minus_eps: String,
one: String,
}
fn without_last(a: &str) -> &str {
&a[..a.len() - 1]
}
fn make_fraction_strings(prefix: &str, eps_frac: &str) -> Fractions {
let eps_frac_compl: String = eps_frac
.chars()
.map(|digit| (b'0' + b'9' - digit as u8) as char)
.collect();
let eps_frac_compl = String::from(without_last(&eps_frac_compl)) + "5";
let zero = String::from("0.") + without_last(&eps_frac) + "499999";
let eps = String::from("0.") + eps_frac;
let one_minus_eps = String::from(prefix) + without_last(&eps_frac_compl) + "499999";
let one = String::from(prefix) + &eps_frac_compl;
Fractions {
zero,
eps,
one_minus_eps,
one,
}
}
// check that for example for four fractional bits,
// * 0.0312499999 (just below 1/32) is parsed as 0 and
// * 0.03125 (exactly 1/32) is parsed as 0.0625 (1/16)
#[test]
fn check_exact_decimal() {
use crate::types::*;
let prefix0 = String::from("0.");
let prefix4 = String::from("15.");
let prefix8 = format!("{}.", !0u8);
let prefix16 = format!("{}.", !0u16);
let prefix28 = format!("{}.", !0u32 >> 4);
let prefix32 = format!("{}.", !0u32);
let prefix64 = format!("{}.", !0u64);
let prefix124 = format!("{}.", !0u128 >> 4);
let prefix128 = format!("{}.", !0u128);
// eps0 = 0.5 >> 0 = 0.5
let eps0 = "5";
// eps4 = 0.5 >> 4 = 0.03125
let eps4 = "03125";
// eps8 = 0.5 >> 8 = 0.001953125
let eps8 = "001953125";
// etc.
let eps16 = "00000762939453125";
let eps28 = "00000000186264514923095703125";
let eps32 = "000000000116415321826934814453125";
let eps64 = "00000000000000000002710505431213761085018632002174854278564453125";
let eps124 = concat!(
"0000000000000000000000000000000000000235098870164457501593747307444449",
"1355637331113544175043017503412556834518909454345703125"
);
let eps128 = concat!(
"0000000000000000000000000000000000000014693679385278593849609206715278",
"07097273331945965109401885939632848021574318408966064453125"
);
let frac_0_8 = make_fraction_strings(&prefix0, eps8);
assert_ok::<U0F8>(&frac_0_8.zero, 0);
assert_ok::<U0F8>(&frac_0_8.eps, 1);
assert_ok::<U0F8>(&frac_0_8.one_minus_eps, !0);
assert_err::<U0F8>(&frac_0_8.one, ParseErrorKind::Overflow);
let frac_4_4 = make_fraction_strings(&prefix4, eps4);
assert_ok::<U4F4>(&frac_4_4.zero, 0);
assert_ok::<U4F4>(&frac_4_4.eps, 1);
assert_ok::<U4F4>(&frac_4_4.one_minus_eps, !0);
assert_err::<U4F4>(&frac_4_4.one, ParseErrorKind::Overflow);
let frac_8_0 = make_fraction_strings(&prefix8, eps0);
assert_ok::<U8F0>(&frac_8_0.zero, 0);
assert_ok::<U8F0>(&frac_8_0.eps, 1);
assert_ok::<U8F0>(&frac_8_0.one_minus_eps, !0);
assert_err::<U8F0>(&frac_8_0.one, ParseErrorKind::Overflow);
let frac_0_32 = make_fraction_strings(&prefix0, eps32);
assert_ok::<U0F32>(&frac_0_32.zero, 0);
assert_ok::<U0F32>(&frac_0_32.eps, 1);
assert_ok::<U0F32>(&frac_0_32.one_minus_eps, !0);
assert_err::<U0F32>(&frac_0_32.one, ParseErrorKind::Overflow);
let frac_4_28 = make_fraction_strings(&prefix4, eps28);
assert_ok::<U4F28>(&frac_4_28.zero, 0);
assert_ok::<U4F28>(&frac_4_28.eps, 1);
assert_ok::<U4F28>(&frac_4_28.one_minus_eps, !0);
assert_err::<U4F28>(&frac_4_28.one, ParseErrorKind::Overflow);
let frac_16_16 = make_fraction_strings(&prefix16, eps16);
assert_ok::<U16F16>(&frac_16_16.zero, 0);
assert_ok::<U16F16>(&frac_16_16.eps, 1);
assert_ok::<U16F16>(&frac_16_16.one_minus_eps, !0);
assert_err::<U16F16>(&frac_16_16.one, ParseErrorKind::Overflow);
let frac_28_4 = make_fraction_strings(&prefix28, eps4);
assert_ok::<U28F4>(&frac_28_4.zero, 0);
assert_ok::<U28F4>(&frac_28_4.eps, 1);
assert_ok::<U28F4>(&frac_28_4.one_minus_eps, !0);
assert_err::<U28F4>(&frac_28_4.one, ParseErrorKind::Overflow);
let frac_32_0 = make_fraction_strings(&prefix32, eps0);
assert_ok::<U32F0>(&frac_32_0.zero, 0);
assert_ok::<U32F0>(&frac_32_0.eps, 1);
assert_ok::<U32F0>(&frac_32_0.one_minus_eps, !0);
assert_err::<U32F0>(&frac_32_0.one, ParseErrorKind::Overflow);
let frac_0_128 = make_fraction_strings(&prefix0, eps128);
assert_ok::<U0F128>(&frac_0_128.zero, 0);
assert_ok::<U0F128>(&frac_0_128.eps, 1);
assert_ok::<U0F128>(&frac_0_128.one_minus_eps, !0);
assert_err::<U0F128>(&frac_0_128.one, ParseErrorKind::Overflow);
let frac_4_124 = make_fraction_strings(&prefix4, eps124);
assert_ok::<U4F124>(&frac_4_124.zero, 0);
assert_ok::<U4F124>(&frac_4_124.eps, 1);
assert_ok::<U4F124>(&frac_4_124.one_minus_eps, !0);
assert_err::<U4F124>(&frac_4_124.one, ParseErrorKind::Overflow);
let frac_64_64 = make_fraction_strings(&prefix64, eps64);
assert_ok::<U64F64>(&frac_64_64.zero, 0);
assert_ok::<U64F64>(&frac_64_64.eps, 1);
assert_ok::<U64F64>(&frac_64_64.one_minus_eps, !0);
assert_err::<U64F64>(&frac_64_64.one, ParseErrorKind::Overflow);
let frac_124_4 = make_fraction_strings(&prefix124, eps4);
assert_ok::<U124F4>(&frac_124_4.zero, 0);
assert_ok::<U124F4>(&frac_124_4.eps, 1);
assert_ok::<U124F4>(&frac_124_4.one_minus_eps, !0);
assert_err::<U124F4>(&frac_124_4.one, ParseErrorKind::Overflow);
let frac_128_0 = make_fraction_strings(&prefix128, eps0);
assert_ok::<U128F0>(&frac_128_0.zero, 0);
assert_ok::<U128F0>(&frac_128_0.eps, 1);
assert_ok::<U128F0>(&frac_128_0.one_minus_eps, !0);
assert_err::<U128F0>(&frac_128_0.one, ParseErrorKind::Overflow);
}
}

View File

@ -205,6 +205,9 @@ additional terms or conditions.
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
#[cfg(test)]
extern crate std;
#[macro_use]
mod macros;

View File

@ -90,6 +90,7 @@ pub trait SealedInt: Copy {
fn is_zero(self) -> bool;
fn is_negative(self) -> bool;
fn checked_add(self, val: Self) -> Option<Self>;
fn overflowing_add(self, val: Self) -> (Self, bool);
fn leading_zeros(self) -> u32;
fn to_fixed_dir_overflow(
@ -172,6 +173,11 @@ macro_rules! sealed_int {
<$Int>::checked_add(self, val)
}
#[inline]
fn overflowing_add(self, val: $Int) -> ($Int, bool) {
<$Int>::overflowing_add(self, val)
}
#[inline]
fn leading_zeros(self) -> u32 {
<$Int>::leading_zeros(self)