]> git.lizzy.rs Git - rust.git/commitdiff
Add various methods to Bignum:
authorRobin Kruppe <robin.kruppe@gmail.com>
Wed, 1 Jul 2015 21:38:43 +0000 (21:38 +0000)
committerRobin Kruppe <robin.kruppe@gmail.com>
Sat, 8 Aug 2015 15:15:19 +0000 (17:15 +0200)
- 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.

src/libcore/num/flt2dec/bignum.rs
src/libcoretest/num/flt2dec/bignum.rs

index 33c5a3ded980fe0547d76477a7618114ec99e785..15980f925fa031ae8ebe170d2aff78298ff34c70 100644 (file)
@@ -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 @@ fn full_div_rem(self, other: $ty, borrow: $ty) -> ($ty, $ty) {
 //  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 @@ pub fn from_u64(mut v: u64) -> $name {
                 $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 @@ pub fn add<'a>(&'a mut self, other: &$name) -> &'a mut $name {
                 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 @@ pub fn mul_pow2<'a>(&'a mut self, bits: usize) -> &'a mut $name {
                 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 @@ pub fn mul_digits<'a>(&'a mut self, other: &[$ty]) -> &'a mut $name {
 
                 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 @@ pub fn div_rem_small<'a>(&'a mut self, other: $ty) -> (&'a mut $name, $ty) {
                 }
                 (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);
 }
-
index 09a1ed41dadd17c7f0dcbc244096fd00b62b0e1d..31065b2898f82bdf8a8ff020e006b1714d2091cc 100644 (file)
@@ -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));