diff --git a/src/lib.rs b/src/lib.rs index 724798d..5707719 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -947,7 +947,7 @@ macro_rules! fixed { return saturated; } let (neg, abs_128, overflow) = - ::to_neg_abs_overflow(val, frac_bits, int_bits); + ::to_fixed_neg_abs_overflow(val, frac_bits, int_bits); if overflow { return saturated; } @@ -1085,7 +1085,7 @@ macro_rules! fixed { panic!("{} is not finite", val); } let (neg, abs_128, mut overflow) = - ::to_neg_abs_overflow(val, frac_bits, int_bits); + ::to_fixed_neg_abs_overflow(val, frac_bits, int_bits); let abs_bits = abs_128 as <<$Fixed as SealedFixed>::Bits as SealedInt>::Unsigned; @@ -1173,14 +1173,7 @@ macro_rules! fixed { { let frac_bits = Self::frac_bits(); let int_bits = Self::int_bits(); - let (neg, int, frac) = self.parts(); - let abs = if frac_bits == 0 { - int - } else if int_bits == 0 { - frac - } else { - (int << frac_bits) | (frac >> int_bits) - }; + let (neg, abs) = self.to_bits().neg_abs(); SealedFloat::from_neg_abs(neg, u128::from(abs), frac_bits, int_bits) } } diff --git a/src/sealed_fixed.rs b/src/sealed_fixed.rs index b6cb283..e3ef908 100644 --- a/src/sealed_fixed.rs +++ b/src/sealed_fixed.rs @@ -56,6 +56,8 @@ pub trait SealedFixed: Copy { ::Unsigned, ::Unsigned, ); + + fn to_neg_abs_overflow(self, frac_bits: u32, int_bits: u32) -> (bool, u128, bool); } macro_rules! sealed_fixed { @@ -98,6 +100,38 @@ macro_rules! sealed_fixed { }; (neg, int_abs, frac_abs) } + + #[inline] + fn to_neg_abs_overflow( + self, + dst_frac_bits: u32, + dst_int_bits: u32, + ) -> (bool, u128, bool) { + let src_frac_bits = ::frac_bits(); + let src_bits = Self::Bits::nbits() as i32; + let dst_bits = (dst_frac_bits + dst_int_bits) as i32; + + if SealedInt::is_zero(self.to_bits()) { + return (false, 0, false); + } + + let (neg, mut abs) = SealedInt::neg_abs(self.to_bits()); + let leading_zeros = abs.leading_zeros(); + abs <<= leading_zeros; + let need_to_shr = + leading_zeros as i32 + src_frac_bits as i32 - dst_frac_bits as i32; + let overflow = src_bits - need_to_shr > dst_bits; + let abs = if need_to_shr == 0 { + u128::from(abs) + } else if need_to_shr < 0 && -need_to_shr < 128 { + u128::from(abs) << -need_to_shr + } else if need_to_shr > 0 && need_to_shr < 128 { + u128::from(abs) >> need_to_shr + } else { + 0 + }; + (neg, abs, overflow) + } } }; } diff --git a/src/sealed_float.rs b/src/sealed_float.rs index e25bf88..7d64799 100644 --- a/src/sealed_float.rs +++ b/src/sealed_float.rs @@ -49,7 +49,7 @@ pub trait SealedFloat: Copy + Display + Debug { fn from_neg_abs(neg: bool, abs: u128, frac_bits: u32, int_bits: u32) -> Self; // self must be finite, otherwise meaningless results are returned - fn to_neg_abs_overflow(self, frac_bits: u32, int_bits: u32) -> (bool, u128, bool); + fn to_fixed_neg_abs_overflow(self, frac_bits: u32, int_bits: u32) -> (bool, u128, bool); } macro_rules! sealed_float { @@ -170,7 +170,11 @@ macro_rules! sealed_float { Self::from_bits(bits_sign | bits_exp_mantissa) } - fn to_neg_abs_overflow(self, frac_bits: u32, int_bits: u32) -> (bool, u128, bool) { + fn to_fixed_neg_abs_overflow( + self, + frac_bits: u32, + int_bits: u32, + ) -> (bool, u128, bool) { let float_bits = Self::Bits::nbits() as i32; let prec = Self::prec() as i32; let fix_bits = (frac_bits + int_bits) as i32; diff --git a/src/sealed_int.rs b/src/sealed_int.rs index a00e6f7..eb8c1b2 100644 --- a/src/sealed_int.rs +++ b/src/sealed_int.rs @@ -21,6 +21,9 @@ pub trait SealedInt: Copy + Ord { fn one_shl(shift: u32) -> Self; fn all_ones_shl(shift: u32) -> Self; fn is_zero(self) -> bool; + + fn to_fixed_neg_abs_overflow(self, frac_bits: u32, int_bits: u32) -> (bool, u128, bool); + fn neg_abs(self) -> (bool, Self::Unsigned); fn from_neg_abs(neg: bool, abs: Self::Unsigned) -> Self; @@ -60,6 +63,33 @@ macro_rules! sealed_int { self == 0 } + #[inline] + fn to_fixed_neg_abs_overflow( + self, + frac_bits: u32, + int_bits: u32, + ) -> (bool, u128, bool) { + let src_bits = ::nbits() as i32; + let dst_bits = (frac_bits + int_bits) as i32; + + let (neg, mut abs) = SealedInt::neg_abs(self); + let leading_zeros = abs.leading_zeros(); + abs <<= leading_zeros; + let need_to_shr = + leading_zeros as i32 - frac_bits as i32; + let overflow = src_bits - need_to_shr > dst_bits; + let abs = if need_to_shr == 0 { + u128::from(abs) + } else if need_to_shr < 0 && -need_to_shr < 128 { + u128::from(abs) << -need_to_shr + } else if need_to_shr > 0 && need_to_shr < 128 { + u128::from(abs) >> need_to_shr + } else { + 0 + }; + (neg, abs, overflow) + } + $($rest)* } }; @@ -138,6 +168,22 @@ impl SealedInt for bool { !self } + #[inline] + fn to_fixed_neg_abs_overflow(self, frac_bits: u32, int_bits: u32) -> (bool, u128, bool) { + if !self { + return (false, 0, false); + } + let overflow = int_bits == 0; + let abs = if frac_bits == 0 { + 1u128 + } else if frac_bits < 128 { + 1u128 << frac_bits + } else { + 0 + }; + (false, abs, overflow) + } + #[inline] fn neg_abs(self) -> (bool, bool) { (false, self)