support for u128 and i128 division
This commit is contained in:
parent
49a83c4174
commit
e4a1afe07e
21
src/arith.rs
21
src/arith.rs
|
@ -21,6 +21,7 @@ use core::ops::{
|
||||||
Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
|
Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
|
||||||
};
|
};
|
||||||
use frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8};
|
use frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8};
|
||||||
|
use wide_div::WideDivRem;
|
||||||
use {
|
use {
|
||||||
FixedHelper, FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32,
|
FixedHelper, FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32,
|
||||||
FixedU64, FixedU8,
|
FixedU64, FixedU8,
|
||||||
|
@ -748,7 +749,7 @@ impl FallbackHelper for i128 {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! mul_div_fallback {
|
macro_rules! mul_div_fallback {
|
||||||
($Single:ty, $Signedness:tt) => {
|
($Single:ty, $Uns:ty, $Signedness:tt) => {
|
||||||
impl MulDivDir for $Single {
|
impl MulDivDir for $Single {
|
||||||
fn mul_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) {
|
fn mul_dir(self, rhs: $Single, frac_bits: u32) -> ($Single, Ordering) {
|
||||||
if frac_bits == 0 {
|
if frac_bits == 0 {
|
||||||
|
@ -812,7 +813,15 @@ macro_rules! mul_div_fallback {
|
||||||
} }
|
} }
|
||||||
(ans, dir)
|
(ans, dir)
|
||||||
} else {
|
} 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! { u16, u32, Unsigned }
|
||||||
mul_div_widen! { u32, u64, Unsigned }
|
mul_div_widen! { u32, u64, Unsigned }
|
||||||
mul_div_widen! { u64, u128, 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! { i8, i16, Signed }
|
||||||
mul_div_widen! { i16, i32, Signed }
|
mul_div_widen! { i16, i32, Signed }
|
||||||
mul_div_widen! { i32, i64, Signed }
|
mul_div_widen! { i32, i64, Signed }
|
||||||
mul_div_widen! { i64, i128, Signed }
|
mul_div_widen! { i64, i128, Signed }
|
||||||
mul_div_fallback! { i128, Signed }
|
mul_div_fallback! { i128, u128, Signed }
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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 << 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);
|
||||||
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 | 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 << 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);
|
||||||
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 | 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));
|
||||||
|
|
|
@ -161,6 +161,7 @@ mod display;
|
||||||
mod float;
|
mod float;
|
||||||
pub mod frac;
|
pub mod frac;
|
||||||
mod helper;
|
mod helper;
|
||||||
|
mod wide_div;
|
||||||
|
|
||||||
use arith::MulDivDir;
|
use arith::MulDivDir;
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
|
|
|
@ -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
|
||||||
|
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||||
|
// <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
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<U>: 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue