diff --git a/src/arith.rs b/src/arith.rs new file mode 100644 index 0000000..1bd04f5 --- /dev/null +++ b/src/arith.rs @@ -0,0 +1,787 @@ +// Copyright © 2018 Trevor Spiteri + +// This library is free software: you can redistribute it and/or +// modify it under the terms of either +// +// * the Apache License, Version 2.0 or +// * the MIT License +// +// at your option. +// +// You should have recieved copies of the Apache License and the MIT +// License along with the library. If not, see +// and +// . + +use frac::Unsigned; +use std::cmp::Ordering; +use std::iter::{Product, Sum}; +use std::mem; +use std::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, +}; +use { + FixedHelper, FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, + FixedU64, FixedU8, +}; + +macro_rules! refs { + (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { + impl<'a, Frac: Unsigned> $Imp<$Fixed> for &'a $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: $Fixed) -> $Fixed { + <$Fixed as $Imp<$Fixed>>::$method(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> $Imp<&'a $Fixed> for $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: &$Fixed) -> $Fixed { + <$Fixed as $Imp<$Fixed>>::$method(self, *rhs) + } + } + + impl<'a, 'b, Frac: Unsigned> $Imp<&'a $Fixed> for &'b $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: &$Fixed) -> $Fixed { + <$Fixed as $Imp<$Fixed>>::$method(*self, *rhs) + } + } + }; +} + +macro_rules! refs_assign { + (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { + impl<'a, Frac: Unsigned> $Imp<&'a $Fixed> for $Fixed { + #[inline] + fn $method(&mut self, rhs: &$Fixed) { + <$Fixed as $Imp<$Fixed>>::$method(self, *rhs); + } + } + }; +} + +macro_rules! pass { + (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { + impl $Imp<$Fixed> for $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: $Fixed) -> $Fixed { + $Fixed::from_bits(<$Inner as $Imp<$Inner>>::$method( + self.to_bits(), + rhs.to_bits(), + )) + } + } + + refs! { impl $Imp for $Fixed($Inner) { $method } } + }; +} + +macro_rules! pass_assign { + (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { + impl $Imp<$Fixed> for $Fixed { + #[inline] + fn $method(&mut self, rhs: $Fixed) { + <$Inner as $Imp<$Inner>>::$method(&mut (self.0).0, rhs.to_bits()); + } + } + + refs_assign! { impl $Imp for $Fixed($Inner) { $method } } + }; +} + +macro_rules! pass_one { + (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { + impl $Imp for $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self) -> $Fixed { + $Fixed::from_bits(<$Inner as $Imp>::$method(self.to_bits())) + } + } + + impl<'a, Frac: Unsigned> $Imp for &'a $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self) -> $Fixed { + <$Fixed as $Imp>::$method(*self) + } + } + }; +} + +macro_rules! shift { + (impl $Imp:ident < $Rhs:ty > for $Fixed:ident($Inner:ty) { $method:ident }) => { + impl $Imp<$Rhs> for $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: $Rhs) -> $Fixed { + $Fixed::from_bits(<$Inner as $Imp<$Rhs>>::$method(self.to_bits(), rhs)) + } + } + + impl<'a, Frac: Unsigned> $Imp<$Rhs> for &'a $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: $Rhs) -> $Fixed { + <$Fixed as $Imp<$Rhs>>::$method(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> $Imp<&'a $Rhs> for $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: &$Rhs) -> $Fixed { + <$Fixed as $Imp<$Rhs>>::$method(self, *rhs) + } + } + + impl<'a, 'b, Frac: Unsigned> $Imp<&'a $Rhs> for &'b $Fixed { + type Output = $Fixed; + #[inline] + fn $method(self, rhs: &$Rhs) -> $Fixed { + <$Fixed as $Imp<$Rhs>>::$method(*self, *rhs) + } + } + }; +} + +macro_rules! shift_assign { + (impl $Imp:ident < $Rhs:ty > for $Fixed:ident($Inner:ty) { $method:ident }) => { + impl $Imp<$Rhs> for $Fixed { + #[inline] + fn $method(&mut self, rhs: $Rhs) { + <$Inner as $Imp<$Rhs>>::$method(&mut (self.0).0, rhs); + } + } + + impl<'a, Frac: Unsigned> $Imp<&'a $Rhs> for $Fixed { + #[inline] + fn $method(&mut self, rhs: &$Rhs) { + <$Fixed as $Imp<$Rhs>>::$method(self, *rhs); + } + } + }; +} + +macro_rules! shift_all { + ( + impl {$Imp:ident, $ImpAssign:ident}<{$($Rhs:ty),*}> + for $Fixed:ident($Inner:ty) + { $method:ident, $method_assign:ident } + ) => { $( + shift! { impl $Imp<$Rhs> for $Fixed($Inner) { $method } } + shift_assign! { impl $ImpAssign<$Rhs> for $Fixed($Inner) { $method_assign } } + )* }; +} + +macro_rules! fixed_arith { + ($Fixed:ident($Inner:ty, $bits_count:expr), $Signedness:tt) => { + if_signed! { + $Signedness => pass_one! { impl Neg for $Fixed($Inner) { neg } } + } + + pass! { impl Add for $Fixed($Inner) { add } } + pass_assign! { impl AddAssign for $Fixed($Inner) { add_assign } } + pass! { impl Sub for $Fixed($Inner) { sub } } + pass_assign! { impl SubAssign for $Fixed($Inner) { sub_assign } } + + impl Mul<$Fixed> for $Fixed { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: $Fixed) -> $Fixed { + let (ans, dir) = self.to_bits().mul_dir(rhs.to_bits(), Frac::to_u32()); + debug_assert!(dir == Ordering::Equal, "overflow"); + $Fixed::from_bits(ans) + } + } + + refs! { impl Mul for $Fixed($Inner) { mul } } + + impl MulAssign<$Fixed> for $Fixed { + #[inline] + fn mul_assign(&mut self, rhs: $Fixed) { + *self = <$Fixed as Mul<$Fixed>>::mul(*self, rhs) + } + } + + refs_assign! { impl MulAssign for $Fixed($Inner) { mul_assign } } + + impl Div<$Fixed> for $Fixed { + type Output = $Fixed; + #[inline] + fn div(self, rhs: $Fixed) -> $Fixed { + let (ans, dir) = self.to_bits().div_dir(rhs.to_bits(), Frac::to_u32()); + debug_assert!(dir == Ordering::Equal, "overflow"); + $Fixed::from_bits(ans) + } + } + + refs! { impl Div for $Fixed($Inner) { div } } + + impl DivAssign<$Fixed> for $Fixed { + #[inline] + fn div_assign(&mut self, rhs: $Fixed) { + *self = <$Fixed as Div<$Fixed>>::div(*self, rhs) + } + } + + refs_assign! { impl DivAssign for $Fixed($Inner) { div_assign } } + + pass_one! { impl Not for $Fixed($Inner) { not } } + pass! { impl BitAnd for $Fixed($Inner) { bitand } } + pass_assign! { impl BitAndAssign for $Fixed($Inner) { bitand_assign } } + pass! { impl BitOr for $Fixed($Inner) { bitor } } + pass_assign! { impl BitOrAssign for $Fixed($Inner) { bitor_assign } } + pass! { impl BitXor for $Fixed($Inner) { bitxor } } + pass_assign! { impl BitXorAssign for $Fixed($Inner) { bitxor_assign } } + + impl Mul<$Inner> for $Fixed { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: $Inner) -> $Fixed { + $Fixed::from_bits(self.to_bits() * rhs) + } + } + + impl Mul<$Fixed> for $Inner { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: $Fixed) -> $Fixed { + <$Fixed as Mul<$Inner>>::mul(rhs, self) + } + } + + impl<'a, Frac: Unsigned> Mul<$Inner> for &'a $Fixed { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: $Inner) -> $Fixed { + <$Fixed as Mul<$Inner>>::mul(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> Mul<&'a $Fixed> for $Inner { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: &$Fixed) -> $Fixed { + <$Fixed as Mul<$Inner>>::mul(*rhs, self) + } + } + + impl<'a, Frac: Unsigned> Mul<&'a $Inner> for $Fixed { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: &$Inner) -> $Fixed { + <$Fixed as Mul<$Inner>>::mul(self, *rhs) + } + } + + impl<'a, Frac: Unsigned> Mul<$Fixed> for &'a $Inner { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: $Fixed) -> $Fixed { + <$Fixed as Mul<$Inner>>::mul(rhs, *self) + } + } + + impl<'a, 'b, Frac: Unsigned> Mul<&'a $Inner> for &'b $Fixed { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: &$Inner) -> $Fixed { + <$Fixed as Mul<$Inner>>::mul(*self, *rhs) + } + } + + impl<'a, 'b, Frac: Unsigned> Mul<&'a $Fixed> for &'b $Inner { + type Output = $Fixed; + #[inline] + fn mul(self, rhs: &$Fixed) -> $Fixed { + <$Fixed as Mul<$Inner>>::mul(*rhs, *self) + } + } + + impl MulAssign<$Inner> for $Fixed { + #[inline] + fn mul_assign(&mut self, rhs: $Inner) { + *self = <$Fixed as Mul<$Inner>>::mul(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> MulAssign<&'a $Inner> for $Fixed { + #[inline] + fn mul_assign(&mut self, rhs: &$Inner) { + *self = <$Fixed as Mul<$Inner>>::mul(*self, *rhs) + } + } + + impl Div<$Inner> for $Fixed { + type Output = $Fixed; + #[inline] + fn div(self, rhs: $Inner) -> $Fixed { + $Fixed::from_bits(self.to_bits() / rhs) + } + } + + impl<'a, Frac: Unsigned> Div<$Inner> for &'a $Fixed { + type Output = $Fixed; + #[inline] + fn div(self, rhs: $Inner) -> $Fixed { + <$Fixed as Div<$Inner>>::div(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> Div<&'a $Inner> for $Fixed { + type Output = $Fixed; + #[inline] + fn div(self, rhs: &$Inner) -> $Fixed { + <$Fixed as Div<$Inner>>::div(self, *rhs) + } + } + impl<'a, 'b, Frac: Unsigned> Div<&'a $Inner> for &'b $Fixed { + type Output = $Fixed; + #[inline] + fn div(self, rhs: &$Inner) -> $Fixed { + <$Fixed as Div<$Inner>>::div(*self, *rhs) + } + } + + impl DivAssign<$Inner> for $Fixed { + #[inline] + fn div_assign(&mut self, rhs: $Inner) { + *self = <$Fixed as Div<$Inner>>::div(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> DivAssign<&'a $Inner> for $Fixed { + #[inline] + fn div_assign(&mut self, rhs: &$Inner) { + *self = <$Fixed as Div<$Inner>>::div(*self, *rhs) + } + } + + impl Rem<$Inner> for $Fixed { + type Output = $Fixed; + #[inline] + fn rem(self, rhs: $Inner) -> $Fixed { + $Fixed::from_bits(self.to_bits() % rhs) + } + } + + impl<'a, Frac: Unsigned> Rem<$Inner> for &'a $Fixed { + type Output = $Fixed; + #[inline] + fn rem(self, rhs: $Inner) -> $Fixed { + <$Fixed as Rem<$Inner>>::rem(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> Rem<&'a $Inner> for $Fixed { + type Output = $Fixed; + #[inline] + fn rem(self, rhs: &$Inner) -> $Fixed { + <$Fixed as Rem<$Inner>>::rem(self, *rhs) + } + } + impl<'a, 'b, Frac: Unsigned> Rem<&'a $Inner> for &'b $Fixed { + type Output = $Fixed; + #[inline] + fn rem(self, rhs: &$Inner) -> $Fixed { + <$Fixed as Rem<$Inner>>::rem(*self, *rhs) + } + } + + impl RemAssign<$Inner> for $Fixed { + #[inline] + fn rem_assign(&mut self, rhs: $Inner) { + *self = <$Fixed as Rem<$Inner>>::rem(*self, rhs) + } + } + + impl<'a, Frac: Unsigned> RemAssign<&'a $Inner> for $Fixed { + #[inline] + fn rem_assign(&mut self, rhs: &$Inner) { + *self = <$Fixed as Rem<$Inner>>::rem(*self, *rhs) + } + } + + shift_all! { + impl {Shl, ShlAssign}<{ + i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize + }> for $Fixed($Inner) { + shl, shl_assign + } + } + shift_all! { + impl {Shr, ShrAssign}<{ + i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize + }> for $Fixed($Inner) { + shr, shr_assign + } + } + + impl Sum<$Fixed> for $Fixed { + fn sum>>(iter: I) -> $Fixed { + iter.fold($Fixed::from_bits(0), Add::add) + } + } + + impl<'a, Frac: Unsigned + 'a> Sum<&'a $Fixed> for $Fixed { + fn sum>>(iter: I) -> $Fixed { + iter.fold($Fixed::from_bits(0), Add::add) + } + } + + impl Product<$Fixed> for $Fixed { + fn product>>(mut iter: I) -> $Fixed { + match iter.next() { + None => <$Fixed as FixedHelper>::one().expect("overflow"), + Some(first) => iter.fold(first, Mul::mul), + } + } + } + + impl<'a, Frac: Unsigned + 'a> Product<&'a $Fixed> for $Fixed { + fn product>>(mut iter: I) -> $Fixed { + match iter.next() { + None => <$Fixed as FixedHelper>::one().expect("overflow"), + Some(first) => iter.fold(*first, Mul::mul), + } + } + } + }; +} + +fixed_arith! { FixedU8(u8, 8), Unsigned } +fixed_arith! { FixedU16(u16, 16), Unsigned } +fixed_arith! { FixedU32(u32, 32), Unsigned } +fixed_arith! { FixedU64(u64, 64), Unsigned } +fixed_arith! { FixedU128(u128, 128), Unsigned } +fixed_arith! { FixedI8(i8, 8), Signed } +fixed_arith! { FixedI16(i16, 16), Signed } +fixed_arith! { FixedI32(i32, 32), Signed } +fixed_arith! { FixedI64(i64, 64), Signed } +fixed_arith! { FixedI128(i128, 128), Signed } + +pub(crate) trait MulDivDir: Sized { + fn mul_dir(self, rhs: Self, frac_bits: u32) -> (Self, Ordering); + fn div_dir(self, rhs: Self, frac_bits: u32) -> (Self, Ordering); +} + +macro_rules! mul_div_widen { + ($Single:ty, $Double:ty, $Signedness:tt) => { + impl MulDivDir for $Single { + #[inline] + fn mul_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { + const BITS: u32 = mem::size_of::<$Single>() as u32 * 8; + let int_bits: u32 = BITS - frac_bits; + let lhs2 = self as $Double; + let rhs2 = rhs as $Double << int_bits; + let (prod2, overflow) = lhs2.overflowing_mul(rhs2); + let dir; + if_unsigned! { $Signedness => { + dir = if !overflow { + Ordering::Equal + } else { + Ordering::Less + }; + } } + if_signed! { $Signedness => { + dir = if !overflow { + Ordering::Equal + } else if (self < 0) == (rhs < 0) { + Ordering::Less + } else { + Ordering::Greater + }; + } } + ((prod2 >> BITS) as $Single, dir) + } + + #[inline] + fn div_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { + let lhs2 = self as $Double << frac_bits; + let rhs2 = rhs as $Double; + let quot2 = lhs2 / rhs2; + let quot = quot2 as $Single; + let dir = (quot as $Double).cmp("2); + (quot, dir) + } + } + }; +} + +trait FallbackHelper: Sized { + type Unsigned; + fn hi_lo(self) -> (Self, Self); + fn shift_lo_up(self) -> Self; + fn shift_lo_up_unsigned(self) -> Self::Unsigned; + fn combine_lo_then_shl(self, lo: Self::Unsigned, shift: u32) -> (Self, Ordering); + fn carrying_add(self, other: Self) -> (Self, Self); +} + +impl FallbackHelper for u128 { + type Unsigned = u128; + #[inline] + fn hi_lo(self) -> (u128, u128) { + (self >> 64, self & !(!0 << 64)) + } + + #[inline] + fn shift_lo_up(self) -> u128 { + debug_assert!(self >> 64 == 0); + self << 64 + } + + #[inline] + fn shift_lo_up_unsigned(self) -> u128 { + debug_assert!(self >> 64 == 0); + self << 64 + } + + #[inline] + fn combine_lo_then_shl(self, lo: u128, shift: u32) -> (u128, Ordering) { + if shift == 128 { + return (self, Ordering::Equal); + } + if shift == 0 { + return (lo, 0.cmp(&self)); + } + let lo = lo >> shift; + let hi = self << (128 - shift); + (lo | hi, 0.cmp(&(self >> shift))) + } + + #[inline] + fn carrying_add(self, rhs: u128) -> (u128, u128) { + let (sum, overflow) = self.overflowing_add(rhs); + let carry = if overflow { 1 } else { 0 }; + (sum, carry) + } +} + +impl FallbackHelper for i128 { + type Unsigned = u128; + #[inline] + fn hi_lo(self) -> (i128, i128) { + (self >> 64, self & !(!0 << 64)) + } + + #[inline] + fn shift_lo_up(self) -> i128 { + debug_assert!(self >> 64 == 0); + self << 64 + } + + #[inline] + fn shift_lo_up_unsigned(self) -> u128 { + debug_assert!(self >> 64 == 0); + (self << 64) as u128 + } + + #[inline] + fn combine_lo_then_shl(self, lo: u128, shift: u32) -> (i128, Ordering) { + if shift == 128 { + return (self, Ordering::Equal); + } + if shift == 0 { + let ans = lo as i128; + return (ans, (ans >> 64 >> 64).cmp(&self)); + } + let lo = (lo >> shift) as i128; + let hi = self << (128 - shift); + let ans = lo | hi; + (ans, (ans >> 64 >> 64).cmp(&(self >> shift))) + } + + #[inline] + fn carrying_add(self, rhs: i128) -> (i128, i128) { + let (sum, overflow) = self.overflowing_add(rhs); + let carry = if overflow { + if sum < 0 { + 1 + } else { + -1 + } + } else { + 0 + }; + (sum, carry) + } +} + +macro_rules! mul_div_fallback { + ($Single:ty, $Signedness:tt) => { + impl MulDivDir for $Single { + fn mul_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { + if frac_bits == 0 { + let (ans, overflow) = self.overflowing_mul(rhs); + let dir; + if_unsigned! { $Signedness => { + dir = if !overflow { + Ordering::Equal + } else { + Ordering::Less + }; + } } + if_signed! { $Signedness => { + dir = if !overflow { + Ordering::Equal + } else if (self < 0) == (rhs < 0) { + Ordering::Less + } else { + Ordering::Greater + }; + } } + (ans, dir) + } else { + let (lh, ll) = self.hi_lo(); + let (rh, rl) = rhs.hi_lo(); + let ll_rl = ll.wrapping_mul(rl); + let lh_rl = lh.wrapping_mul(rl); + let ll_rh = ll.wrapping_mul(rh); + let lh_rh = lh.wrapping_mul(rh); + let col01 = ll_rl as <$Single as FallbackHelper>::Unsigned; + let (col12, carry_col3) = lh_rl.carrying_add(ll_rh); + let col23 = lh_rh; + let (col12_hi, col12_lo) = col12.hi_lo(); + let col12_lo_up = col12_lo.shift_lo_up_unsigned(); + let (ans01, carry_col2) = col01.carrying_add(col12_lo_up); + let carries = carry_col2 as $Single + carry_col3.shift_lo_up(); + let ans23 = col23.wrapping_add(carries).wrapping_add(col12_hi); + + ans23.combine_lo_then_shl(ans01, frac_bits) + } + } + + fn div_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { + if frac_bits == 0 { + let (ans, overflow) = self.overflowing_div(rhs); + let dir; + if_unsigned! { $Signedness => { + dir = if !overflow { + Ordering::Equal + } else { + Ordering::Less + }; + } } + if_signed! { $Signedness => { + dir = if !overflow { + Ordering::Equal + } else if (self < 0) == (rhs < 0) { + Ordering::Less + } else { + Ordering::Greater + }; + } } + (ans, dir) + } else { + unimplemented!() + } + } + } + }; +} + +mul_div_widen! { u8, u16, Unsigned } +mul_div_widen! { u16, u32, Unsigned } +mul_div_widen! { u32, u64, Unsigned } +mul_div_widen! { u64, u128, Unsigned } +mul_div_fallback! { u128, Unsigned } +mul_div_widen! { i8, i16, Signed } +mul_div_widen! { i16, i32, Signed } +mul_div_widen! { i32, i64, Signed } +mul_div_widen! { i64, i128, Signed } +mul_div_fallback! { i128, Signed } + +#[cfg(test)] +mod tests { + use *; + + #[test] + fn fixed_u16() { + use frac::U7 as Frac; + let frac = Frac::to_u32(); + let a = 12; + let b = 4; + let af = FixedU16::::from_bits(a << Frac::to_u32()); + let bf = FixedU16::::from_bits(b << Frac::to_u32()); + assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); + assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); + assert_eq!((af * bf).to_bits(), (a << frac) * b); + assert_eq!((af / bf).to_bits(), (a << frac) / b); + assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); + assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); + assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); + assert_eq!((!af).to_bits(), !(a << frac)); + assert_eq!((af << 4u8).to_bits(), (a << frac) << 4); + assert_eq!((af >> 4i128).to_bits(), (a << frac) >> 4); + } + + #[test] + fn fixed_i16() { + use frac::U7 as Frac; + let frac = Frac::to_u32(); + let a = 12; + let b = 4; + for &pair in &[(a, b), (a, -b), (-a, b), (-a, -b)] { + let (a, b) = pair; + let af = FixedI16::::from_bits(a << frac); + let bf = FixedI16::::from_bits(b << frac); + assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); + assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); + assert_eq!((af * bf).to_bits(), (a << frac) * b); + assert_eq!((af / bf).to_bits(), (a << frac) / b); + assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); + assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); + assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); + assert_eq!((-af).to_bits(), -(a << frac)); + assert_eq!((!af).to_bits(), !(a << frac)); + assert_eq!((af << 4u8).to_bits(), (a << frac) << 4); + assert_eq!((af >> 4i128).to_bits(), (a << frac) >> 4); + } + } + + #[test] + fn fixed_u128() { + use frac::U7 as Frac; + let frac = Frac::to_u32(); + let a = 0x0003456789abcdef_0123456789abcdef_u128; + let b = 5; + let af = FixedU128::::from_bits(a << frac); + let bf = FixedU128::::from_bits(b << frac); + assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); + assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); + assert_eq!((af * bf).to_bits(), (a << frac) * b); + // assert_eq!((af / bf).to_bits(), (a << frac) / b); + assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); + assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); + assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); + assert_eq!((!af).to_bits(), !(a << frac)); + } + + #[test] + fn fixed_i128() { + use frac::U7 as Frac; + let frac = Frac::to_u32(); + let a = 0x0003456789abcdef_0123456789abcdef_i128; + let b = 5; + for &pair in &[(a, b), (a, -b), (-a, b), (-a, -b)] { + let (a, b) = pair; + let af = FixedI128::::from_bits(a << frac); + let bf = FixedI128::::from_bits(b << frac); + assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); + assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); + assert_eq!((af * bf).to_bits(), (a << frac) * b); + // assert_eq!((af / bf).to_bits(), (a << frac) / b); + assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); + assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); + assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); + assert_eq!((!af).to_bits(), !(a << frac)); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 8a3d97d..1dc38e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,25 +117,6 @@ additional terms or conditions. extern crate typenum; -mod cmp; -mod display; -pub mod frac; -mod helper; - -use frac::Unsigned; -use helper::FixedHelper; -use std::cmp::Ordering; -use std::f32; -use std::f64; -use std::hash::{Hash, Hasher}; -use std::iter::{Product, Sum}; -use std::marker::PhantomData; -use std::mem; -use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, - Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, -}; - macro_rules! if_signed { (Signed => $($rem:tt)+) => { $($rem)+ @@ -151,94 +132,20 @@ macro_rules! if_unsigned { }; } -macro_rules! refs { - (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { - impl<'a, Frac: Unsigned> $Imp<$Fixed> for &'a $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: $Fixed) -> $Fixed { - <$Fixed as $Imp<$Fixed>>::$method(*self, rhs) - } - } +mod arith; +mod cmp; +mod display; +pub mod frac; +mod helper; - impl<'a, Frac: Unsigned> $Imp<&'a $Fixed> for $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: &$Fixed) -> $Fixed { - <$Fixed as $Imp<$Fixed>>::$method(self, *rhs) - } - } - - impl<'a, 'b, Frac: Unsigned> $Imp<&'a $Fixed> for &'b $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: &$Fixed) -> $Fixed { - <$Fixed as $Imp<$Fixed>>::$method(*self, *rhs) - } - } - }; -} - -macro_rules! refs_assign { - (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { - impl<'a, Frac: Unsigned> $Imp<&'a $Fixed> for $Fixed { - #[inline] - fn $method(&mut self, rhs: &$Fixed) { - <$Fixed as $Imp<$Fixed>>::$method(self, *rhs); - } - } - }; -} - -macro_rules! pass { - (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { - impl $Imp<$Fixed> for $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: $Fixed) -> $Fixed { - $Fixed::from_bits(<$Inner as $Imp<$Inner>>::$method( - self.to_bits(), - rhs.to_bits(), - )) - } - } - - refs! { impl $Imp for $Fixed($Inner) { $method } } - }; -} - -macro_rules! pass_assign { - (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { - impl $Imp<$Fixed> for $Fixed { - #[inline] - fn $method(&mut self, rhs: $Fixed) { - <$Inner as $Imp<$Inner>>::$method(&mut (self.0).0, rhs.to_bits()); - } - } - - refs_assign! { impl $Imp for $Fixed($Inner) { $method } } - }; -} - -macro_rules! pass_one { - (impl $Imp:ident for $Fixed:ident($Inner:ty) { $method:ident }) => { - impl $Imp for $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self) -> $Fixed { - $Fixed::from_bits(<$Inner as $Imp>::$method(self.to_bits())) - } - } - - impl<'a, Frac: Unsigned> $Imp for &'a $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self) -> $Fixed { - <$Fixed as $Imp>::$method(*self) - } - } - }; -} +use arith::MulDivDir; +use frac::Unsigned; +use helper::FixedHelper; +use std::cmp::Ordering; +use std::f32; +use std::f64; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; macro_rules! pass_method { ($comment:expr, $Fixed:ident($Inner:ty) => fn $method:ident()) => { @@ -274,71 +181,6 @@ macro_rules! pass_method { }; } -macro_rules! shift { - (impl $Imp:ident < $Rhs:ty > for $Fixed:ident($Inner:ty) { $method:ident }) => { - impl $Imp<$Rhs> for $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: $Rhs) -> $Fixed { - $Fixed::from_bits(<$Inner as $Imp<$Rhs>>::$method(self.to_bits(), rhs)) - } - } - - impl<'a, Frac: Unsigned> $Imp<$Rhs> for &'a $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: $Rhs) -> $Fixed { - <$Fixed as $Imp<$Rhs>>::$method(*self, rhs) - } - } - - impl<'a, Frac: Unsigned> $Imp<&'a $Rhs> for $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: &$Rhs) -> $Fixed { - <$Fixed as $Imp<$Rhs>>::$method(self, *rhs) - } - } - - impl<'a, 'b, Frac: Unsigned> $Imp<&'a $Rhs> for &'b $Fixed { - type Output = $Fixed; - #[inline] - fn $method(self, rhs: &$Rhs) -> $Fixed { - <$Fixed as $Imp<$Rhs>>::$method(*self, *rhs) - } - } - }; -} - -macro_rules! shift_assign { - (impl $Imp:ident < $Rhs:ty > for $Fixed:ident($Inner:ty) { $method:ident }) => { - impl $Imp<$Rhs> for $Fixed { - #[inline] - fn $method(&mut self, rhs: $Rhs) { - <$Inner as $Imp<$Rhs>>::$method(&mut (self.0).0, rhs); - } - } - - impl<'a, Frac: Unsigned> $Imp<&'a $Rhs> for $Fixed { - #[inline] - fn $method(&mut self, rhs: &$Rhs) { - <$Fixed as $Imp<$Rhs>>::$method(self, *rhs); - } - } - }; -} - -macro_rules! shift_all { - ( - impl {$Imp:ident, $ImpAssign:ident}<{$($Rhs:ty),*}> - for $Fixed:ident($Inner:ty) - { $method:ident, $method_assign:ident } - ) => { $( - shift! { impl $Imp<$Rhs> for $Fixed($Inner) { $method } } - shift_assign! { impl $ImpAssign<$Rhs> for $Fixed($Inner) { $method_assign } } - )* }; -} - macro_rules! doc_comment { ($x:expr, $($tt:tt)*) => { #[doc = $x] @@ -1236,278 +1078,6 @@ macro_rules! fixed { } } } - - if_signed! { - $Signedness => pass_one! { impl Neg for $Fixed($Inner) { neg } } - } - - pass! { impl Add for $Fixed($Inner) { add } } - pass_assign! { impl AddAssign for $Fixed($Inner) { add_assign } } - pass! { impl Sub for $Fixed($Inner) { sub } } - pass_assign! { impl SubAssign for $Fixed($Inner) { sub_assign } } - - impl Mul<$Fixed> for $Fixed { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: $Fixed) -> $Fixed { - let (ans, dir) = self.to_bits().mul_dir(rhs.to_bits(), Frac::to_u32()); - debug_assert!(dir == Ordering::Equal, "overflow"); - $Fixed::from_bits(ans) - } - } - - refs! { impl Mul for $Fixed($Inner) { mul } } - - impl MulAssign<$Fixed> for $Fixed { - #[inline] - fn mul_assign(&mut self, rhs: $Fixed) { - *self = <$Fixed as Mul<$Fixed>>::mul(*self, rhs) - } - } - - refs_assign! { impl MulAssign for $Fixed($Inner) { mul_assign } } - - impl Div<$Fixed> for $Fixed { - type Output = $Fixed; - #[inline] - fn div(self, rhs: $Fixed) -> $Fixed { - let (ans, dir) = self.to_bits().div_dir(rhs.to_bits(), Frac::to_u32()); - debug_assert!(dir == Ordering::Equal, "overflow"); - $Fixed::from_bits(ans) - } - } - - refs! { impl Div for $Fixed($Inner) { div } } - - impl DivAssign<$Fixed> for $Fixed { - #[inline] - fn div_assign(&mut self, rhs: $Fixed) { - *self = <$Fixed as Div<$Fixed>>::div(*self, rhs) - } - } - - refs_assign! { impl DivAssign for $Fixed($Inner) { div_assign } } - - pass_one! { impl Not for $Fixed($Inner) { not } } - pass! { impl BitAnd for $Fixed($Inner) { bitand } } - pass_assign! { impl BitAndAssign for $Fixed($Inner) { bitand_assign } } - pass! { impl BitOr for $Fixed($Inner) { bitor } } - pass_assign! { impl BitOrAssign for $Fixed($Inner) { bitor_assign } } - pass! { impl BitXor for $Fixed($Inner) { bitxor } } - pass_assign! { impl BitXorAssign for $Fixed($Inner) { bitxor_assign } } - - impl Mul<$Inner> for $Fixed { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: $Inner) -> $Fixed { - $Fixed::from_bits(self.to_bits() * rhs) - } - } - - impl Mul<$Fixed> for $Inner { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: $Fixed) -> $Fixed { - <$Fixed as Mul<$Inner>>::mul(rhs, self) - } - } - - impl<'a, Frac: Unsigned> Mul<$Inner> for &'a $Fixed { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: $Inner) -> $Fixed { - <$Fixed as Mul<$Inner>>::mul(*self, rhs) - } - } - - impl<'a, Frac: Unsigned> Mul<&'a $Fixed> for $Inner { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: &$Fixed) -> $Fixed { - <$Fixed as Mul<$Inner>>::mul(*rhs, self) - } - } - - impl<'a, Frac: Unsigned> Mul<&'a $Inner> for $Fixed { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: &$Inner) -> $Fixed { - <$Fixed as Mul<$Inner>>::mul(self, *rhs) - } - } - - impl<'a, Frac: Unsigned> Mul<$Fixed> for &'a $Inner { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: $Fixed) -> $Fixed { - <$Fixed as Mul<$Inner>>::mul(rhs, *self) - } - } - - impl<'a, 'b, Frac: Unsigned> Mul<&'a $Inner> for &'b $Fixed { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: &$Inner) -> $Fixed { - <$Fixed as Mul<$Inner>>::mul(*self, *rhs) - } - } - - impl<'a, 'b, Frac: Unsigned> Mul<&'a $Fixed> for &'b $Inner { - type Output = $Fixed; - #[inline] - fn mul(self, rhs: &$Fixed) -> $Fixed { - <$Fixed as Mul<$Inner>>::mul(*rhs, *self) - } - } - - impl MulAssign<$Inner> for $Fixed { - #[inline] - fn mul_assign(&mut self, rhs: $Inner) { - *self = <$Fixed as Mul<$Inner>>::mul(*self, rhs) - } - } - - impl<'a, Frac: Unsigned> MulAssign<&'a $Inner> for $Fixed { - #[inline] - fn mul_assign(&mut self, rhs: &$Inner) { - *self = <$Fixed as Mul<$Inner>>::mul(*self, *rhs) - } - } - - impl Div<$Inner> for $Fixed { - type Output = $Fixed; - #[inline] - fn div(self, rhs: $Inner) -> $Fixed { - $Fixed::from_bits(self.to_bits() / rhs) - } - } - - impl<'a, Frac: Unsigned> Div<$Inner> for &'a $Fixed { - type Output = $Fixed; - #[inline] - fn div(self, rhs: $Inner) -> $Fixed { - <$Fixed as Div<$Inner>>::div(*self, rhs) - } - } - - impl<'a, Frac: Unsigned> Div<&'a $Inner> for $Fixed { - type Output = $Fixed; - #[inline] - fn div(self, rhs: &$Inner) -> $Fixed { - <$Fixed as Div<$Inner>>::div(self, *rhs) - } - } - impl<'a, 'b, Frac: Unsigned> Div<&'a $Inner> for &'b $Fixed { - type Output = $Fixed; - #[inline] - fn div(self, rhs: &$Inner) -> $Fixed { - <$Fixed as Div<$Inner>>::div(*self, *rhs) - } - } - - impl DivAssign<$Inner> for $Fixed { - #[inline] - fn div_assign(&mut self, rhs: $Inner) { - *self = <$Fixed as Div<$Inner>>::div(*self, rhs) - } - } - - impl<'a, Frac: Unsigned> DivAssign<&'a $Inner> for $Fixed { - #[inline] - fn div_assign(&mut self, rhs: &$Inner) { - *self = <$Fixed as Div<$Inner>>::div(*self, *rhs) - } - } - - impl Rem<$Inner> for $Fixed { - type Output = $Fixed; - #[inline] - fn rem(self, rhs: $Inner) -> $Fixed { - $Fixed::from_bits(self.to_bits() % rhs) - } - } - - impl<'a, Frac: Unsigned> Rem<$Inner> for &'a $Fixed { - type Output = $Fixed; - #[inline] - fn rem(self, rhs: $Inner) -> $Fixed { - <$Fixed as Rem<$Inner>>::rem(*self, rhs) - } - } - - impl<'a, Frac: Unsigned> Rem<&'a $Inner> for $Fixed { - type Output = $Fixed; - #[inline] - fn rem(self, rhs: &$Inner) -> $Fixed { - <$Fixed as Rem<$Inner>>::rem(self, *rhs) - } - } - impl<'a, 'b, Frac: Unsigned> Rem<&'a $Inner> for &'b $Fixed { - type Output = $Fixed; - #[inline] - fn rem(self, rhs: &$Inner) -> $Fixed { - <$Fixed as Rem<$Inner>>::rem(*self, *rhs) - } - } - - impl RemAssign<$Inner> for $Fixed { - #[inline] - fn rem_assign(&mut self, rhs: $Inner) { - *self = <$Fixed as Rem<$Inner>>::rem(*self, rhs) - } - } - - impl<'a, Frac: Unsigned> RemAssign<&'a $Inner> for $Fixed { - #[inline] - fn rem_assign(&mut self, rhs: &$Inner) { - *self = <$Fixed as Rem<$Inner>>::rem(*self, *rhs) - } - } - - shift_all! { - impl {Shl, ShlAssign}<{ - i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize - }> for $Fixed($Inner) { - shl, shl_assign - } - } - shift_all! { - impl {Shr, ShrAssign}<{ - i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize - }> for $Fixed($Inner) { - shr, shr_assign - } - } - - impl Sum<$Fixed> for $Fixed { - fn sum>>(iter: I) -> $Fixed { - iter.fold($Fixed::from_bits(0), Add::add) - } - } - - impl<'a, Frac: Unsigned + 'a> Sum<&'a $Fixed> for $Fixed { - fn sum>>(iter: I) -> $Fixed { - iter.fold($Fixed::from_bits(0), Add::add) - } - } - - impl Product<$Fixed> for $Fixed { - fn product>>(mut iter: I) -> $Fixed { - match iter.next() { - None => <$Fixed as FixedHelper>::one().expect("overflow"), - Some(first) => iter.fold(first, Mul::mul), - } - } - } - - impl<'a, Frac: Unsigned + 'a> Product<&'a $Fixed> for $Fixed { - fn product>>(mut iter: I) -> $Fixed { - match iter.next() { - None => <$Fixed as FixedHelper>::one().expect("overflow"), - Some(first) => iter.fold(*first, Mul::mul), - } - } - } }; } @@ -1522,324 +1092,10 @@ fixed! { "A 32-bit fixed-point signed integer", FixedI32(i32, 32), Signed } fixed! { "A 64-bit fixed-point signed integer", FixedI64(i64, 64), Signed } fixed! { "A 128-bit fixed-point signed integer", FixedI128(i128, 128), Signed } -trait MulDivDir: Sized { - fn mul_dir(self, rhs: Self, frac_bits: u32) -> (Self, Ordering); - fn div_dir(self, rhs: Self, frac_bits: u32) -> (Self, Ordering); -} - -macro_rules! mul_div_widen { - ($Single:ty, $Double:ty, $Signedness:tt) => { - impl MulDivDir for $Single { - #[inline] - fn mul_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { - const BITS: u32 = mem::size_of::<$Single>() as u32 * 8; - let int_bits: u32 = BITS - frac_bits; - let lhs2 = self as $Double; - let rhs2 = rhs as $Double << int_bits; - let (prod2, overflow) = lhs2.overflowing_mul(rhs2); - let dir; - if_unsigned! { $Signedness => { - dir = if !overflow { - Ordering::Equal - } else { - Ordering::Less - }; - } } - if_signed! { $Signedness => { - dir = if !overflow { - Ordering::Equal - } else if (self < 0) == (rhs < 0) { - Ordering::Less - } else { - Ordering::Greater - }; - } } - ((prod2 >> BITS) as $Single, dir) - } - - #[inline] - fn div_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { - let lhs2 = self as $Double << frac_bits; - let rhs2 = rhs as $Double; - let quot2 = lhs2 / rhs2; - let quot = quot2 as $Single; - let dir = (quot as $Double).cmp("2); - (quot, dir) - } - } - }; -} - -trait FallbackHelper: Sized { - type Unsigned; - fn hi_lo(self) -> (Self, Self); - fn shift_lo_up(self) -> Self; - fn shift_lo_up_unsigned(self) -> Self::Unsigned; - fn combine_lo_then_shl(self, lo: Self::Unsigned, shift: u32) -> (Self, Ordering); - fn carrying_add(self, other: Self) -> (Self, Self); -} - -impl FallbackHelper for u128 { - type Unsigned = u128; - #[inline] - fn hi_lo(self) -> (u128, u128) { - (self >> 64, self & !(!0 << 64)) - } - - #[inline] - fn shift_lo_up(self) -> u128 { - debug_assert!(self >> 64 == 0); - self << 64 - } - - #[inline] - fn shift_lo_up_unsigned(self) -> u128 { - debug_assert!(self >> 64 == 0); - self << 64 - } - - #[inline] - fn combine_lo_then_shl(self, lo: u128, shift: u32) -> (u128, Ordering) { - if shift == 128 { - return (self, Ordering::Equal); - } - if shift == 0 { - return (lo, 0.cmp(&self)); - } - let lo = lo >> shift; - let hi = self << (128 - shift); - (lo | hi, 0.cmp(&(self >> shift))) - } - - #[inline] - fn carrying_add(self, rhs: u128) -> (u128, u128) { - let (sum, overflow) = self.overflowing_add(rhs); - let carry = if overflow { 1 } else { 0 }; - (sum, carry) - } -} - -impl FallbackHelper for i128 { - type Unsigned = u128; - #[inline] - fn hi_lo(self) -> (i128, i128) { - (self >> 64, self & !(!0 << 64)) - } - - #[inline] - fn shift_lo_up(self) -> i128 { - debug_assert!(self >> 64 == 0); - self << 64 - } - - #[inline] - fn shift_lo_up_unsigned(self) -> u128 { - debug_assert!(self >> 64 == 0); - (self << 64) as u128 - } - - #[inline] - fn combine_lo_then_shl(self, lo: u128, shift: u32) -> (i128, Ordering) { - if shift == 128 { - return (self, Ordering::Equal); - } - if shift == 0 { - let ans = lo as i128; - return (ans, (ans >> 64 >> 64).cmp(&self)); - } - let lo = (lo >> shift) as i128; - let hi = self << (128 - shift); - let ans = lo | hi; - (ans, (ans >> 64 >> 64).cmp(&(self >> shift))) - } - - #[inline] - fn carrying_add(self, rhs: i128) -> (i128, i128) { - let (sum, overflow) = self.overflowing_add(rhs); - let carry = if overflow { - if sum < 0 { - 1 - } else { - -1 - } - } else { - 0 - }; - (sum, carry) - } -} - -macro_rules! mul_div_fallback { - ($Single:ty, $Signedness:tt) => { - impl MulDivDir for $Single { - fn mul_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { - if frac_bits == 0 { - let (ans, overflow) = self.overflowing_mul(rhs); - let dir; - if_unsigned! { $Signedness => { - dir = if !overflow { - Ordering::Equal - } else { - Ordering::Less - }; - } } - if_signed! { $Signedness => { - dir = if !overflow { - Ordering::Equal - } else if (self < 0) == (rhs < 0) { - Ordering::Less - } else { - Ordering::Greater - }; - } } - (ans, dir) - } else { - let (lh, ll) = self.hi_lo(); - let (rh, rl) = rhs.hi_lo(); - let ll_rl = ll.wrapping_mul(rl); - let lh_rl = lh.wrapping_mul(rl); - let ll_rh = ll.wrapping_mul(rh); - let lh_rh = lh.wrapping_mul(rh); - let col01 = ll_rl as <$Single as FallbackHelper>::Unsigned; - let (col12, carry_col3) = lh_rl.carrying_add(ll_rh); - let col23 = lh_rh; - let (col12_hi, col12_lo) = col12.hi_lo(); - let col12_lo_up = col12_lo.shift_lo_up_unsigned(); - let (ans01, carry_col2) = col01.carrying_add(col12_lo_up); - let carries = carry_col2 as $Single + carry_col3.shift_lo_up(); - let ans23 = col23.wrapping_add(carries).wrapping_add(col12_hi); - - ans23.combine_lo_then_shl(ans01, frac_bits) - } - } - - fn div_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { - if frac_bits == 0 { - let (ans, overflow) = self.overflowing_div(rhs); - let dir; - if_unsigned! { $Signedness => { - dir = if !overflow { - Ordering::Equal - } else { - Ordering::Less - }; - } } - if_signed! { $Signedness => { - dir = if !overflow { - Ordering::Equal - } else if (self < 0) == (rhs < 0) { - Ordering::Less - } else { - Ordering::Greater - }; - } } - (ans, dir) - } else { - unimplemented!() - } - } - } - }; -} - -mul_div_widen! { u8, u16, Unsigned } -mul_div_widen! { u16, u32, Unsigned } -mul_div_widen! { u32, u64, Unsigned } -mul_div_widen! { u64, u128, Unsigned } -mul_div_fallback! { u128, Unsigned } -mul_div_widen! { i8, i16, Signed } -mul_div_widen! { i16, i32, Signed } -mul_div_widen! { i32, i64, Signed } -mul_div_widen! { i64, i128, Signed } -mul_div_fallback! { i128, Signed } - #[cfg(test)] mod tests { use *; - #[test] - fn fixed_u16() { - use frac::U7 as Frac; - let frac = Frac::to_u32(); - let a = 12; - let b = 4; - let af = FixedU16::::from_bits(a << Frac::to_u32()); - let bf = FixedU16::::from_bits(b << Frac::to_u32()); - assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); - assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); - assert_eq!((af * bf).to_bits(), (a << frac) * b); - assert_eq!((af / bf).to_bits(), (a << frac) / b); - assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); - assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); - assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); - assert_eq!((!af).to_bits(), !(a << frac)); - assert_eq!((af << 4u8).to_bits(), (a << frac) << 4); - assert_eq!((af >> 4i128).to_bits(), (a << frac) >> 4); - } - - #[test] - fn fixed_i16() { - use frac::U7 as Frac; - let frac = Frac::to_u32(); - let a = 12; - let b = 4; - for &pair in &[(a, b), (a, -b), (-a, b), (-a, -b)] { - let (a, b) = pair; - let af = FixedI16::::from_bits(a << frac); - let bf = FixedI16::::from_bits(b << frac); - assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); - assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); - assert_eq!((af * bf).to_bits(), (a << frac) * b); - assert_eq!((af / bf).to_bits(), (a << frac) / b); - assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); - assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); - assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); - assert_eq!((-af).to_bits(), -(a << frac)); - assert_eq!((!af).to_bits(), !(a << frac)); - assert_eq!((af << 4u8).to_bits(), (a << frac) << 4); - assert_eq!((af >> 4i128).to_bits(), (a << frac) >> 4); - } - } - - #[test] - fn fixed_u128() { - use frac::U7 as Frac; - let frac = Frac::to_u32(); - let a = 0x0003456789abcdef_0123456789abcdef_u128; - let b = 5; - let af = FixedU128::::from_bits(a << frac); - let bf = FixedU128::::from_bits(b << frac); - assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); - assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); - assert_eq!((af * bf).to_bits(), (a << frac) * b); - // assert_eq!((af / bf).to_bits(), (a << frac) / b); - assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); - assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); - assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); - assert_eq!((!af).to_bits(), !(a << frac)); - } - - #[test] - fn fixed_i128() { - use frac::U7 as Frac; - let frac = Frac::to_u32(); - let a = 0x0003456789abcdef_0123456789abcdef_i128; - let b = 5; - for &pair in &[(a, b), (a, -b), (-a, b), (-a, -b)] { - let (a, b) = pair; - let af = FixedI128::::from_bits(a << frac); - let bf = FixedI128::::from_bits(b << frac); - assert_eq!((af + bf).to_bits(), (a << frac) + (b << frac)); - assert_eq!((af - bf).to_bits(), (a << frac) - (b << frac)); - assert_eq!((af * bf).to_bits(), (a << frac) * b); - // assert_eq!((af / bf).to_bits(), (a << frac) / b); - assert_eq!((af & bf).to_bits(), (a << frac) & (b << frac)); - assert_eq!((af | bf).to_bits(), (a << frac) | (b << frac)); - assert_eq!((af ^ bf).to_bits(), (a << frac) ^ (b << frac)); - assert_eq!((!af).to_bits(), !(a << frac)); - } - } - #[test] fn to_f32() { use frac::U7 as Frac;