diff --git a/src/arith.rs b/src/arith.rs index 07453d4..cd152e8 100644 --- a/src/arith.rs +++ b/src/arith.rs @@ -21,6 +21,7 @@ use core::ops::{ Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; use frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8}; +use wide_div::WideDivRem; use { FixedHelper, FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64, FixedU8, @@ -748,7 +749,7 @@ impl FallbackHelper for i128 { } macro_rules! mul_div_fallback { - ($Single:ty, $Signedness:tt) => { + ($Single:ty, $Uns:ty, $Signedness:tt) => { impl MulDivDir for $Single { fn mul_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) { if frac_bits == 0 { @@ -812,7 +813,15 @@ macro_rules! mul_div_fallback { } } (ans, dir) } else { - unimplemented!() + const BITS: u32 = mem::size_of::<$Single>() as u32 * 8; + let lhs2 = (self >> (BITS - frac_bits), (self << frac_bits) as $Uns); + let (quot2, _) = rhs.div_rem_from(lhs2); + let quot = quot2.1 as $Single; + let quot2_ret = (quot >> (BITS / 2) >> (BITS / 2), quot2.1); + let dir = (quot2_ret.0) + .cmp("2.0) + .then((quot2_ret.1).cmp("2.1)); + (quot, dir) } } } @@ -823,12 +832,12 @@ 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_fallback! { u128, 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 } +mul_div_fallback! { i128, u128, Signed } #[cfg(test)] mod tests { @@ -892,7 +901,7 @@ mod tests { 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); 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)); @@ -921,7 +930,7 @@ mod tests { 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); 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)); diff --git a/src/lib.rs b/src/lib.rs index b060687..ac7805a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,6 +161,7 @@ mod display; mod float; pub mod frac; mod helper; +mod wide_div; use arith::MulDivDir; use core::cmp::Ordering; diff --git a/src/wide_div.rs b/src/wide_div.rs new file mode 100644 index 0000000..ba9c511 --- /dev/null +++ b/src/wide_div.rs @@ -0,0 +1,292 @@ +// 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 +// . + +trait DivHalf: Copy { + fn hi(self) -> Self; + fn lo(self) -> Self; + fn up_lo(self, lo: Self) -> Self; + fn div_half(&mut self, d: Self, next_half: Self) -> Self; + fn normalize(&mut self, n1: &mut Self, n0: &mut Self) -> (Self, u32); + fn unnormalize(self, zeros: u32) -> Self; +} + +macro_rules! div_half { + ($($T:ty: $n:expr),*) => { $( + impl DivHalf for $T { + #[inline] + fn hi(self) -> $T { + self >> ($n / 2) + } + + #[inline] + fn lo(self) -> $T { + self & !(!0 << ($n / 2)) + } + + #[inline] + fn up_lo(self, lo: $T) -> $T { + self << ($n / 2) | lo + } + + #[inline] + fn div_half(&mut self, d: $T, next_half: $T) -> $T { + let dh = d.hi(); + let (mut q, rr) = (*self / dh, *self % dh); + let m = q * d.lo(); + *self = rr.up_lo(next_half); + if *self < m { + q -= 1; + *self = match self.overflowing_add(d) { + (r, false) if r < m => { + q -= 1; + r.wrapping_add(d) + } + (r, _) => r, + }; + } + *self = self.wrapping_sub(m); + q + } + + #[inline] + fn normalize(&mut self, n1: &mut $T, n0: &mut $T) -> ($T, u32) { + assert!(*self != 0, "division by zero"); + let zeros = self.leading_zeros(); + if zeros == 0 { + (0, 0) + } else { + *self <<= zeros; + let n2 = *n1 >> ($n - zeros); + *n1 = *n1 << zeros | *n0 >> ($n - zeros); + *n0 <<= zeros; + (n2, zeros) + } + } + + #[inline] + fn unnormalize(self, zeros: u32) -> Self { + self >> zeros + } + } + )* }; +} + +div_half!{ u8: 8, u16: 16, u32: 32, u64: 64, u128: 128 } + +trait NegAbs { + type Abs; + fn neg_abs(self) -> (bool, Self::Abs); + fn from_neg_abs(neg: bool, abs: Self::Abs) -> Self; +} + +macro_rules! neg_abs { + ($($S:ty: $U:ty),*) => { $( + impl NegAbs for $S { + type Abs = $U; + + #[inline] + fn neg_abs(self) -> (bool, $U) { + if self < 0 { + (true, self.wrapping_neg() as $U) + } else { + (false, self as $U) + } + } + + #[inline] + fn from_neg_abs(neg: bool, abs: $U) -> $S { + if neg { + abs.wrapping_neg() as $S + } else { + abs as $S + } + } + } + + impl NegAbs for ($S, $U) { + type Abs = ($U, $U); + + #[inline] + fn neg_abs(self) -> (bool, ($U, $U)) { + if self.0 < 0 { + match self.1.overflowing_neg() { + (n, true) => (true, (!self.0 as $U, n)), + (n, false) => (true, (self.0.wrapping_neg() as $U, n)), + } + } else { + (false, (self.0 as $U, self.1)) + } + } + + #[inline] + fn from_neg_abs(neg: bool, abs: ($U, $U)) -> ($S, $U) { + if neg { + match abs.1.overflowing_neg() { + (n, true) => (!abs.0 as $S, n), + (n, false) => (abs.0.wrapping_neg() as $S, n), + } + } else { + (abs.0 as $S, abs.1) + } + } + } + )* }; +} + +neg_abs!{ i8: u8, i16: u16, i32: u32, i64: u64, i128: u128 } + +pub trait WideDivRem: Sized { + fn div_rem_from(self, dividend: (Self, U)) -> ((Self, U), Self); +} + +macro_rules! unsigned_wide_div_rem { + ($($U:ty),*) => { $( + impl WideDivRem<$U> for $U { + fn div_rem_from(self, dividend: ($U, $U)) -> (($U, $U), $U) { + let (mut n1, mut n0, mut d) = (dividend.0, dividend.1, self); + let (mut r, zeros) = d.normalize(&mut n1, &mut n0); + + let q1h = r.div_half(d, n1.hi()); + let q1l = r.div_half(d, n1.lo()); + let q0h = r.div_half(d, n0.hi()); + let q0l = r.div_half(d, n0.lo()); + ((q1h.up_lo(q1l), q0h.up_lo(q0l)), r.unnormalize(zeros)) + } + } + )* }; +} + +macro_rules! signed_wide_div_rem { + ($($S:ty: $U:ty),*) => { $( + impl WideDivRem<$U> for $S { + fn div_rem_from(self, dividend: ($S, $U)) -> (($S, $U), $S) { + let (n_neg, n_abs) = dividend.neg_abs(); + let (d_neg, d_abs) = self.neg_abs(); + let (q, r) = d_abs.div_rem_from(n_abs); + ( + NegAbs::from_neg_abs(n_neg != d_neg, q), + NegAbs::from_neg_abs(n_neg, r), + ) + } + } + )* }; +} + +unsigned_wide_div_rem! { u8, u16, u32, u64, u128 } +signed_wide_div_rem! { i8: u8, i16: u16, i32: u32, i64: u64, i128: u128 } + +#[cfg(test)] +mod tests { + use super::WideDivRem; + + fn check_8((n1, n0): (u8, u8), d: u8) -> ((u8, u8), u8) { + let n = u16::from(n1) << 8 | u16::from(n0); + let d = u16::from(d); + let (q, r) = (n / d, n % d); + (((q >> 8) as u8, q as u8), r as u8) + } + + fn check_16((n1, n0): (u16, u16), d: u16) -> ((u16, u16), u16) { + let n = u32::from(n1) << 16 | u32::from(n0); + let d = u32::from(d); + let (q, r) = (n / d, n % d); + (((q >> 16) as u16, q as u16), r as u16) + } + + fn check_64((n1, n0): (u64, u64), d: u64) -> ((u64, u64), u64) { + let n = u128::from(n1) << 64 | u128::from(n0); + let d = u128::from(d); + let (q, r) = (n / d, n % d); + (((q >> 64) as u64, q as u64), r as u64) + } + + fn icheck_8((n1, n0): (i8, u8), d: i8) -> ((i8, u8), i8) { + let n = i16::from(n1) << 8 | i16::from(n0); + let d = i16::from(d); + let (q, r) = (n / d, n % d); + (((q >> 8) as i8, q as u8), r as i8) + } + + fn icheck_16((n1, n0): (i16, u16), d: i16) -> ((i16, u16), i16) { + let n = i32::from(n1) << 16 | i32::from(n0); + let d = i32::from(d); + let (q, r) = (n / d, n % d); + (((q >> 16) as i16, q as u16), r as i16) + } + + fn icheck_64((n1, n0): (i64, u64), d: i64) -> ((i64, u64), i64) { + let n = i128::from(n1) << 64 | i128::from(n0); + let d = i128::from(d); + let (q, r) = (n / d, n % d); + (((q >> 64) as i64, q as u64), r as i64) + } + + #[test] + fn test_wide_div_rem() { + for d in 1..=255 { + for n1 in (0..=255).step_by(15) { + for n0 in (0..=255).step_by(15) { + let qr = d.div_rem_from((n1, n0)); + let check = check_8((n1, n0), d); + assert_eq!(qr, check); + + let d = u16::from(d) << 8 | 1; + let n1 = u16::from(n1) << 8 | 1; + let n0 = u16::from(n0) << 8 | 1; + let qr = d.div_rem_from((n1, n0)); + let check = check_16((n1, n0), d); + assert_eq!(qr, check); + + let d = u64::from(d) << 48 | 1; + let n1 = u64::from(n1) << 48 | 1; + let n0 = u64::from(n0) << 48 | 1; + let qr = d.div_rem_from((n1, n0)); + let check = check_64((n1, n0), d); + assert_eq!(qr, check); + } + } + } + } + + #[test] + fn test_wide_idiv_rem() { + for d in -128..=127 { + if d == 0 { + continue; + } + for n1 in (-120..=120).step_by(15) { + for n0 in (0..=255).step_by(15) { + let qr = d.div_rem_from((n1, n0)); + let check = icheck_8((n1, n0), d); + assert_eq!(qr, check); + + let d = i16::from(d) << 8 | 1; + let n1 = i16::from(n1) << 8 | 1; + let n0 = u16::from(n0) << 8 | 1; + let qr = d.div_rem_from((n1, n0)); + let check = icheck_16((n1, n0), d); + assert_eq!(qr, check); + + let d = i64::from(d) << 48 | 1; + let n1 = i64::from(n1) << 48 | 1; + let n0 = u64::from(n0) << 48 | 1; + let qr = d.div_rem_from((n1, n0)); + let check = icheck_64((n1, n0), d); + assert_eq!(qr, check); + } + } + } + } +}