Add various methods to Bignum:
- Exposing digits and individual bits - Counting the number of bits - Add small (digit-sized) values - Multiplication by power of 5 - Division with remainder All are necessary for decimal to floating point conversions. All but the most trivial ones come with tests.
This commit is contained in:
parent
7ff10209aa
commit
7ebd7f3b9a
@ -13,7 +13,7 @@
|
||||
//! This is designed to avoid the heap allocation at expense of stack memory.
|
||||
//! The most used bignum type, `Big32x40`, is limited by 32 × 40 = 1,280 bits
|
||||
//! and will take at most 160 bytes of stack memory. This is more than enough
|
||||
//! for formatting and parsing all possible finite `f64` values.
|
||||
//! for round-tripping all possible finite `f64` values.
|
||||
//!
|
||||
//! In principle it is possible to have multiple bignum types for different
|
||||
//! inputs, but we don't do so to avoid the code bloat. Each bignum is still
|
||||
@ -92,6 +92,14 @@ impl_full_ops! {
|
||||
// u64: add(intrinsics::u64_add_with_overflow), mul/div(u128); // see RFC #521 for enabling this.
|
||||
}
|
||||
|
||||
/// Table of powers of 5 representable in digits. Specifically, the largest {u8, u16, u32} value
|
||||
/// that's a power of five, plus the corresponding exponent. Used in `mul_pow5`.
|
||||
const SMALL_POW5: [(u64, usize); 3] = [
|
||||
(125, 3),
|
||||
(15625, 6),
|
||||
(1_220_703_125, 13),
|
||||
];
|
||||
|
||||
macro_rules! define_bignum {
|
||||
($name:ident: type=$ty:ty, n=$n:expr) => (
|
||||
/// Stack-allocated arbitrary-precision (up to certain limit) integer.
|
||||
@ -135,9 +143,53 @@ macro_rules! define_bignum {
|
||||
$name { size: sz, base: base }
|
||||
}
|
||||
|
||||
/// Return the internal digits as a slice `[a, b, c, ...]` such that the numeric
|
||||
/// value is `a + b * 2^W + c * 2^(2W) + ...` where `W` is the number of bits in
|
||||
/// the digit type.
|
||||
pub fn digits(&self) -> &[$ty] {
|
||||
&self.base[..self.size]
|
||||
}
|
||||
|
||||
/// Return the `i`-th bit where bit 0 is the least significant one.
|
||||
/// In other words, the bit with weight `2^i`.
|
||||
pub fn get_bit(&self, i: usize) -> u8 {
|
||||
use mem;
|
||||
|
||||
let digitbits = mem::size_of::<$ty>() * 8;
|
||||
let d = i / digitbits;
|
||||
let b = i % digitbits;
|
||||
((self.base[d] >> b) & 1) as u8
|
||||
}
|
||||
|
||||
/// Returns true if the bignum is zero.
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.base[..self.size].iter().all(|&v| v == 0)
|
||||
self.digits().iter().all(|&v| v == 0)
|
||||
}
|
||||
|
||||
/// Returns the number of bits necessary to represent this value. Note that zero
|
||||
/// is considered to need 0 bits.
|
||||
pub fn bit_length(&self) -> usize {
|
||||
use mem;
|
||||
|
||||
let digitbits = mem::size_of::<$ty>()* 8;
|
||||
// Skip over the most significant digits which are zero.
|
||||
let nonzero = match self.digits().iter().rposition(|&x| x != 0) {
|
||||
Some(n) => {
|
||||
let end = self.size - n;
|
||||
&self.digits()[..end]
|
||||
}
|
||||
None => {
|
||||
// There are no non-zero digits, i.e. the number is zero.
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
// This could be optimized with leading_zeros() and bit shifts, but that's
|
||||
// probably not worth the hassle.
|
||||
let mut i = nonzero.len() * digitbits - 1;
|
||||
while self.get_bit(i) == 0 {
|
||||
i -= 1;
|
||||
}
|
||||
i + 1
|
||||
}
|
||||
|
||||
/// Adds `other` to itself and returns its own mutable reference.
|
||||
@ -160,6 +212,24 @@ macro_rules! define_bignum {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_small<'a>(&'a mut self, other: $ty) -> &'a mut $name {
|
||||
use num::flt2dec::bignum::FullOps;
|
||||
|
||||
let (mut carry, v) = self.base[0].full_add(other, false);
|
||||
self.base[0] = v;
|
||||
let mut i = 1;
|
||||
while carry {
|
||||
let (c, v) = self.base[i].full_add(0, carry);
|
||||
self.base[i] = v;
|
||||
carry = c;
|
||||
i += 1;
|
||||
}
|
||||
if i > self.size {
|
||||
self.size = i;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Subtracts `other` from itself and returns its own mutable reference.
|
||||
pub fn sub<'a>(&'a mut self, other: &$name) -> &'a mut $name {
|
||||
use cmp;
|
||||
@ -238,6 +308,34 @@ macro_rules! define_bignum {
|
||||
self
|
||||
}
|
||||
|
||||
/// Multiplies itself by `5^e` and returns its own mutable reference.
|
||||
pub fn mul_pow5<'a>(&'a mut self, mut e: usize) -> &'a mut $name {
|
||||
use mem;
|
||||
use num::flt2dec::bignum::SMALL_POW5;
|
||||
|
||||
// There are exactly n trailing zeros on 2^n, and the only relevant digit sizes
|
||||
// are consecutive powers of two, so this is well suited index for the table.
|
||||
let table_index = mem::size_of::<$ty>().trailing_zeros() as usize;
|
||||
let (small_power, small_e) = SMALL_POW5[table_index];
|
||||
let small_power = small_power as $ty;
|
||||
|
||||
// Multiply with the largest single-digit power as long as possible ...
|
||||
while e >= small_e {
|
||||
self.mul_small(small_power);
|
||||
e -= small_e;
|
||||
}
|
||||
|
||||
// ... then finish off the remainder.
|
||||
let mut rest_power = 1;
|
||||
for _ in 0..e {
|
||||
rest_power *= 5;
|
||||
}
|
||||
self.mul_small(rest_power);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
/// Multiplies itself by a number described by `other[0] + other[1] * 2^W +
|
||||
/// other[2] * 2^(2W) + ...` (where `W` is the number of bits in the digit type)
|
||||
/// and returns its own mutable reference.
|
||||
@ -269,9 +367,9 @@ macro_rules! define_bignum {
|
||||
|
||||
let mut ret = [0; $n];
|
||||
let retsz = if self.size < other.len() {
|
||||
mul_inner(&mut ret, &self.base[..self.size], other)
|
||||
mul_inner(&mut ret, &self.digits(), other)
|
||||
} else {
|
||||
mul_inner(&mut ret, other, &self.base[..self.size])
|
||||
mul_inner(&mut ret, other, &self.digits())
|
||||
};
|
||||
self.base = ret;
|
||||
self.size = retsz;
|
||||
@ -294,6 +392,45 @@ macro_rules! define_bignum {
|
||||
}
|
||||
(self, borrow)
|
||||
}
|
||||
|
||||
/// Divide self by another bignum, overwriting `q` with the quotient and `r` with the
|
||||
/// remainder.
|
||||
pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) {
|
||||
use mem;
|
||||
|
||||
// Stupid slow base-2 long division taken from
|
||||
// https://en.wikipedia.org/wiki/Division_algorithm
|
||||
// FIXME use a greater base ($ty) for the long division.
|
||||
assert!(!d.is_zero());
|
||||
let digitbits = mem::size_of::<$ty>() * 8;
|
||||
for digit in &mut q.base[..] {
|
||||
*digit = 0;
|
||||
}
|
||||
for digit in &mut r.base[..] {
|
||||
*digit = 0;
|
||||
}
|
||||
r.size = d.size;
|
||||
q.size = 1;
|
||||
let mut q_is_zero = true;
|
||||
let end = self.bit_length();
|
||||
for i in (0..end).rev() {
|
||||
r.mul_pow2(1);
|
||||
r.base[0] |= self.get_bit(i) as $ty;
|
||||
if &*r >= d {
|
||||
r.sub(d);
|
||||
// Set bit `i` of q to 1.
|
||||
let digit_idx = i / digitbits;
|
||||
let bit_idx = i % digitbits;
|
||||
if q_is_zero {
|
||||
q.size = digit_idx + 1;
|
||||
q_is_zero = false;
|
||||
}
|
||||
q.base[digit_idx] |= 1 << bit_idx;
|
||||
}
|
||||
}
|
||||
debug_assert!(q.base[q.size..].iter().all(|&d| d == 0));
|
||||
debug_assert!(r.base[r.size..].iter().all(|&d| d == 0));
|
||||
}
|
||||
}
|
||||
|
||||
impl ::cmp::PartialEq for $name {
|
||||
@ -355,4 +492,3 @@ pub mod tests {
|
||||
use prelude::v1::*;
|
||||
define_bignum!(Big8x3: type=u8, n=3);
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,23 @@ fn test_add_overflow_2() {
|
||||
Big::from_u64(0xffffff).add(&Big::from_small(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_small() {
|
||||
assert_eq!(*Big::from_small(3).add_small(4), Big::from_small(7));
|
||||
assert_eq!(*Big::from_small(3).add_small(0), Big::from_small(3));
|
||||
assert_eq!(*Big::from_small(0).add_small(3), Big::from_small(3));
|
||||
assert_eq!(*Big::from_small(7).add_small(250), Big::from_u64(257));
|
||||
assert_eq!(*Big::from_u64(0x7fff).add_small(1), Big::from_u64(0x8000));
|
||||
assert_eq!(*Big::from_u64(0x2ffe).add_small(0x35), Big::from_u64(0x3033));
|
||||
assert_eq!(*Big::from_small(0xdc).add_small(0x89), Big::from_u64(0x165));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_add_small_overflow() {
|
||||
Big::from_u64(0xffffff).add_small(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
assert_eq!(*Big::from_small(7).sub(&Big::from_small(4)), Big::from_small(3));
|
||||
@ -97,6 +114,30 @@ fn test_mul_pow2_overflow_2() {
|
||||
Big::from_u64(0x123).mul_pow2(16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_pow5() {
|
||||
assert_eq!(*Big::from_small(42).mul_pow5(0), Big::from_small(42));
|
||||
assert_eq!(*Big::from_small(1).mul_pow5(2), Big::from_small(25));
|
||||
assert_eq!(*Big::from_small(1).mul_pow5(4), Big::from_u64(25 * 25));
|
||||
assert_eq!(*Big::from_small(4).mul_pow5(3), Big::from_u64(500));
|
||||
assert_eq!(*Big::from_small(140).mul_pow5(2), Big::from_u64(25 * 140));
|
||||
assert_eq!(*Big::from_small(25).mul_pow5(1), Big::from_small(125));
|
||||
assert_eq!(*Big::from_small(125).mul_pow5(7), Big::from_u64(9765625));
|
||||
assert_eq!(*Big::from_small(0).mul_pow5(127), Big::from_small(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_mul_pow5_overflow_1() {
|
||||
Big::from_small(1).mul_pow5(12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_mul_pow5_overflow_2() {
|
||||
Big::from_small(230).mul_pow5(8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_digits() {
|
||||
assert_eq!(*Big::from_small(3).mul_digits(&[5]), Big::from_small(15));
|
||||
@ -132,6 +173,25 @@ fn test_div_rem_small() {
|
||||
(Big::from_u64(0x10000 / 123), (0x10000u64 % 123) as u8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_rem() {
|
||||
fn div_rem(n: u64, d: u64) -> (Big, Big) {
|
||||
let mut q = Big::from_small(42);
|
||||
let mut r = Big::from_small(42);
|
||||
Big::from_u64(n).div_rem(&Big::from_u64(d), &mut q, &mut r);
|
||||
(q, r)
|
||||
}
|
||||
assert_eq!(div_rem(1, 1), (Big::from_small(1), Big::from_small(0)));
|
||||
assert_eq!(div_rem(4, 3), (Big::from_small(1), Big::from_small(1)));
|
||||
assert_eq!(div_rem(1, 7), (Big::from_small(0), Big::from_small(1)));
|
||||
assert_eq!(div_rem(45, 9), (Big::from_small(5), Big::from_small(0)));
|
||||
assert_eq!(div_rem(103, 9), (Big::from_small(11), Big::from_small(4)));
|
||||
assert_eq!(div_rem(123456, 77), (Big::from_u64(1603), Big::from_small(25)));
|
||||
assert_eq!(div_rem(0xffff, 1), (Big::from_u64(0xffff), Big::from_small(0)));
|
||||
assert_eq!(div_rem(0xeeee, 0xffff), (Big::from_small(0), Big::from_u64(0xeeee)));
|
||||
assert_eq!(div_rem(2_000_000, 2), (Big::from_u64(1_000_000), Big::from_u64(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_zero() {
|
||||
assert!(Big::from_small(0).is_zero());
|
||||
@ -141,6 +201,35 @@ fn test_is_zero() {
|
||||
assert!(Big::from_u64(0xffffff).sub(&Big::from_u64(0xffffff)).is_zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_bit() {
|
||||
let x = Big::from_small(0b1101);
|
||||
assert_eq!(x.get_bit(0), 1);
|
||||
assert_eq!(x.get_bit(1), 0);
|
||||
assert_eq!(x.get_bit(2), 1);
|
||||
assert_eq!(x.get_bit(3), 1);
|
||||
let y = Big::from_u64(1 << 15);
|
||||
assert_eq!(y.get_bit(14), 0);
|
||||
assert_eq!(y.get_bit(15), 1);
|
||||
assert_eq!(y.get_bit(16), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_bit_out_of_range() {
|
||||
Big::from_small(42).get_bit(24);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bit_length() {
|
||||
assert_eq!(Big::from_small(0).bit_length(), 0);
|
||||
assert_eq!(Big::from_small(1).bit_length(), 1);
|
||||
assert_eq!(Big::from_small(5).bit_length(), 3);
|
||||
assert_eq!(Big::from_small(0x18).bit_length(), 5);
|
||||
assert_eq!(Big::from_u64(0x4073).bit_length(), 15);
|
||||
assert_eq!(Big::from_u64(0xffffff).bit_length(), 24);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ord() {
|
||||
assert!(Big::from_u64(0) < Big::from_u64(0xffffff));
|
||||
|
Loading…
x
Reference in New Issue
Block a user