Skip to content

Commit 7ebd7f3

Browse files
author
Robin Kruppe
committed
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.
1 parent 7ff1020 commit 7ebd7f3

File tree

2 files changed

+230
-5
lines changed

2 files changed

+230
-5
lines changed

src/libcore/num/flt2dec/bignum.rs

+141-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//! This is designed to avoid the heap allocation at expense of stack memory.
1414
//! The most used bignum type, `Big32x40`, is limited by 32 × 40 = 1,280 bits
1515
//! and will take at most 160 bytes of stack memory. This is more than enough
16-
//! for formatting and parsing all possible finite `f64` values.
16+
//! for round-tripping all possible finite `f64` values.
1717
//!
1818
//! In principle it is possible to have multiple bignum types for different
1919
//! inputs, but we don't do so to avoid the code bloat. Each bignum is still
@@ -92,6 +92,14 @@ impl_full_ops! {
9292
// u64: add(intrinsics::u64_add_with_overflow), mul/div(u128); // see RFC #521 for enabling this.
9393
}
9494

95+
/// Table of powers of 5 representable in digits. Specifically, the largest {u8, u16, u32} value
96+
/// that's a power of five, plus the corresponding exponent. Used in `mul_pow5`.
97+
const SMALL_POW5: [(u64, usize); 3] = [
98+
(125, 3),
99+
(15625, 6),
100+
(1_220_703_125, 13),
101+
];
102+
95103
macro_rules! define_bignum {
96104
($name:ident: type=$ty:ty, n=$n:expr) => (
97105
/// Stack-allocated arbitrary-precision (up to certain limit) integer.
@@ -135,9 +143,53 @@ macro_rules! define_bignum {
135143
$name { size: sz, base: base }
136144
}
137145

146+
/// Return the internal digits as a slice `[a, b, c, ...]` such that the numeric
147+
/// value is `a + b * 2^W + c * 2^(2W) + ...` where `W` is the number of bits in
148+
/// the digit type.
149+
pub fn digits(&self) -> &[$ty] {
150+
&self.base[..self.size]
151+
}
152+
153+
/// Return the `i`-th bit where bit 0 is the least significant one.
154+
/// In other words, the bit with weight `2^i`.
155+
pub fn get_bit(&self, i: usize) -> u8 {
156+
use mem;
157+
158+
let digitbits = mem::size_of::<$ty>() * 8;
159+
let d = i / digitbits;
160+
let b = i % digitbits;
161+
((self.base[d] >> b) & 1) as u8
162+
}
163+
138164
/// Returns true if the bignum is zero.
139165
pub fn is_zero(&self) -> bool {
140-
self.base[..self.size].iter().all(|&v| v == 0)
166+
self.digits().iter().all(|&v| v == 0)
167+
}
168+
169+
/// Returns the number of bits necessary to represent this value. Note that zero
170+
/// is considered to need 0 bits.
171+
pub fn bit_length(&self) -> usize {
172+
use mem;
173+
174+
let digitbits = mem::size_of::<$ty>()* 8;
175+
// Skip over the most significant digits which are zero.
176+
let nonzero = match self.digits().iter().rposition(|&x| x != 0) {
177+
Some(n) => {
178+
let end = self.size - n;
179+
&self.digits()[..end]
180+
}
181+
None => {
182+
// There are no non-zero digits, i.e. the number is zero.
183+
return 0;
184+
}
185+
};
186+
// This could be optimized with leading_zeros() and bit shifts, but that's
187+
// probably not worth the hassle.
188+
let mut i = nonzero.len() * digitbits - 1;
189+
while self.get_bit(i) == 0 {
190+
i -= 1;
191+
}
192+
i + 1
141193
}
142194

143195
/// Adds `other` to itself and returns its own mutable reference.
@@ -160,6 +212,24 @@ macro_rules! define_bignum {
160212
self
161213
}
162214

215+
pub fn add_small<'a>(&'a mut self, other: $ty) -> &'a mut $name {
216+
use num::flt2dec::bignum::FullOps;
217+
218+
let (mut carry, v) = self.base[0].full_add(other, false);
219+
self.base[0] = v;
220+
let mut i = 1;
221+
while carry {
222+
let (c, v) = self.base[i].full_add(0, carry);
223+
self.base[i] = v;
224+
carry = c;
225+
i += 1;
226+
}
227+
if i > self.size {
228+
self.size = i;
229+
}
230+
self
231+
}
232+
163233
/// Subtracts `other` from itself and returns its own mutable reference.
164234
pub fn sub<'a>(&'a mut self, other: &$name) -> &'a mut $name {
165235
use cmp;
@@ -238,6 +308,34 @@ macro_rules! define_bignum {
238308
self
239309
}
240310

311+
/// Multiplies itself by `5^e` and returns its own mutable reference.
312+
pub fn mul_pow5<'a>(&'a mut self, mut e: usize) -> &'a mut $name {
313+
use mem;
314+
use num::flt2dec::bignum::SMALL_POW5;
315+
316+
// There are exactly n trailing zeros on 2^n, and the only relevant digit sizes
317+
// are consecutive powers of two, so this is well suited index for the table.
318+
let table_index = mem::size_of::<$ty>().trailing_zeros() as usize;
319+
let (small_power, small_e) = SMALL_POW5[table_index];
320+
let small_power = small_power as $ty;
321+
322+
// Multiply with the largest single-digit power as long as possible ...
323+
while e >= small_e {
324+
self.mul_small(small_power);
325+
e -= small_e;
326+
}
327+
328+
// ... then finish off the remainder.
329+
let mut rest_power = 1;
330+
for _ in 0..e {
331+
rest_power *= 5;
332+
}
333+
self.mul_small(rest_power);
334+
335+
self
336+
}
337+
338+
241339
/// Multiplies itself by a number described by `other[0] + other[1] * 2^W +
242340
/// other[2] * 2^(2W) + ...` (where `W` is the number of bits in the digit type)
243341
/// and returns its own mutable reference.
@@ -269,9 +367,9 @@ macro_rules! define_bignum {
269367

270368
let mut ret = [0; $n];
271369
let retsz = if self.size < other.len() {
272-
mul_inner(&mut ret, &self.base[..self.size], other)
370+
mul_inner(&mut ret, &self.digits(), other)
273371
} else {
274-
mul_inner(&mut ret, other, &self.base[..self.size])
372+
mul_inner(&mut ret, other, &self.digits())
275373
};
276374
self.base = ret;
277375
self.size = retsz;
@@ -294,6 +392,45 @@ macro_rules! define_bignum {
294392
}
295393
(self, borrow)
296394
}
395+
396+
/// Divide self by another bignum, overwriting `q` with the quotient and `r` with the
397+
/// remainder.
398+
pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) {
399+
use mem;
400+
401+
// Stupid slow base-2 long division taken from
402+
// https://en.wikipedia.org/wiki/Division_algorithm
403+
// FIXME use a greater base ($ty) for the long division.
404+
assert!(!d.is_zero());
405+
let digitbits = mem::size_of::<$ty>() * 8;
406+
for digit in &mut q.base[..] {
407+
*digit = 0;
408+
}
409+
for digit in &mut r.base[..] {
410+
*digit = 0;
411+
}
412+
r.size = d.size;
413+
q.size = 1;
414+
let mut q_is_zero = true;
415+
let end = self.bit_length();
416+
for i in (0..end).rev() {
417+
r.mul_pow2(1);
418+
r.base[0] |= self.get_bit(i) as $ty;
419+
if &*r >= d {
420+
r.sub(d);
421+
// Set bit `i` of q to 1.
422+
let digit_idx = i / digitbits;
423+
let bit_idx = i % digitbits;
424+
if q_is_zero {
425+
q.size = digit_idx + 1;
426+
q_is_zero = false;
427+
}
428+
q.base[digit_idx] |= 1 << bit_idx;
429+
}
430+
}
431+
debug_assert!(q.base[q.size..].iter().all(|&d| d == 0));
432+
debug_assert!(r.base[r.size..].iter().all(|&d| d == 0));
433+
}
297434
}
298435

299436
impl ::cmp::PartialEq for $name {
@@ -355,4 +492,3 @@ pub mod tests {
355492
use prelude::v1::*;
356493
define_bignum!(Big8x3: type=u8, n=3);
357494
}
358-

src/libcoretest/num/flt2dec/bignum.rs

+89
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,23 @@ fn test_add_overflow_2() {
3939
Big::from_u64(0xffffff).add(&Big::from_small(1));
4040
}
4141

42+
#[test]
43+
fn test_add_small() {
44+
assert_eq!(*Big::from_small(3).add_small(4), Big::from_small(7));
45+
assert_eq!(*Big::from_small(3).add_small(0), Big::from_small(3));
46+
assert_eq!(*Big::from_small(0).add_small(3), Big::from_small(3));
47+
assert_eq!(*Big::from_small(7).add_small(250), Big::from_u64(257));
48+
assert_eq!(*Big::from_u64(0x7fff).add_small(1), Big::from_u64(0x8000));
49+
assert_eq!(*Big::from_u64(0x2ffe).add_small(0x35), Big::from_u64(0x3033));
50+
assert_eq!(*Big::from_small(0xdc).add_small(0x89), Big::from_u64(0x165));
51+
}
52+
53+
#[test]
54+
#[should_panic]
55+
fn test_add_small_overflow() {
56+
Big::from_u64(0xffffff).add_small(1);
57+
}
58+
4259
#[test]
4360
fn test_sub() {
4461
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() {
97114
Big::from_u64(0x123).mul_pow2(16);
98115
}
99116

117+
#[test]
118+
fn test_mul_pow5() {
119+
assert_eq!(*Big::from_small(42).mul_pow5(0), Big::from_small(42));
120+
assert_eq!(*Big::from_small(1).mul_pow5(2), Big::from_small(25));
121+
assert_eq!(*Big::from_small(1).mul_pow5(4), Big::from_u64(25 * 25));
122+
assert_eq!(*Big::from_small(4).mul_pow5(3), Big::from_u64(500));
123+
assert_eq!(*Big::from_small(140).mul_pow5(2), Big::from_u64(25 * 140));
124+
assert_eq!(*Big::from_small(25).mul_pow5(1), Big::from_small(125));
125+
assert_eq!(*Big::from_small(125).mul_pow5(7), Big::from_u64(9765625));
126+
assert_eq!(*Big::from_small(0).mul_pow5(127), Big::from_small(0));
127+
}
128+
129+
#[test]
130+
#[should_panic]
131+
fn test_mul_pow5_overflow_1() {
132+
Big::from_small(1).mul_pow5(12);
133+
}
134+
135+
#[test]
136+
#[should_panic]
137+
fn test_mul_pow5_overflow_2() {
138+
Big::from_small(230).mul_pow5(8);
139+
}
140+
100141
#[test]
101142
fn test_mul_digits() {
102143
assert_eq!(*Big::from_small(3).mul_digits(&[5]), Big::from_small(15));
@@ -132,6 +173,25 @@ fn test_div_rem_small() {
132173
(Big::from_u64(0x10000 / 123), (0x10000u64 % 123) as u8));
133174
}
134175

176+
#[test]
177+
fn test_div_rem() {
178+
fn div_rem(n: u64, d: u64) -> (Big, Big) {
179+
let mut q = Big::from_small(42);
180+
let mut r = Big::from_small(42);
181+
Big::from_u64(n).div_rem(&Big::from_u64(d), &mut q, &mut r);
182+
(q, r)
183+
}
184+
assert_eq!(div_rem(1, 1), (Big::from_small(1), Big::from_small(0)));
185+
assert_eq!(div_rem(4, 3), (Big::from_small(1), Big::from_small(1)));
186+
assert_eq!(div_rem(1, 7), (Big::from_small(0), Big::from_small(1)));
187+
assert_eq!(div_rem(45, 9), (Big::from_small(5), Big::from_small(0)));
188+
assert_eq!(div_rem(103, 9), (Big::from_small(11), Big::from_small(4)));
189+
assert_eq!(div_rem(123456, 77), (Big::from_u64(1603), Big::from_small(25)));
190+
assert_eq!(div_rem(0xffff, 1), (Big::from_u64(0xffff), Big::from_small(0)));
191+
assert_eq!(div_rem(0xeeee, 0xffff), (Big::from_small(0), Big::from_u64(0xeeee)));
192+
assert_eq!(div_rem(2_000_000, 2), (Big::from_u64(1_000_000), Big::from_u64(0)));
193+
}
194+
135195
#[test]
136196
fn test_is_zero() {
137197
assert!(Big::from_small(0).is_zero());
@@ -141,6 +201,35 @@ fn test_is_zero() {
141201
assert!(Big::from_u64(0xffffff).sub(&Big::from_u64(0xffffff)).is_zero());
142202
}
143203

204+
#[test]
205+
fn test_get_bit() {
206+
let x = Big::from_small(0b1101);
207+
assert_eq!(x.get_bit(0), 1);
208+
assert_eq!(x.get_bit(1), 0);
209+
assert_eq!(x.get_bit(2), 1);
210+
assert_eq!(x.get_bit(3), 1);
211+
let y = Big::from_u64(1 << 15);
212+
assert_eq!(y.get_bit(14), 0);
213+
assert_eq!(y.get_bit(15), 1);
214+
assert_eq!(y.get_bit(16), 0);
215+
}
216+
217+
#[test]
218+
#[should_panic]
219+
fn test_get_bit_out_of_range() {
220+
Big::from_small(42).get_bit(24);
221+
}
222+
223+
#[test]
224+
fn test_bit_length() {
225+
assert_eq!(Big::from_small(0).bit_length(), 0);
226+
assert_eq!(Big::from_small(1).bit_length(), 1);
227+
assert_eq!(Big::from_small(5).bit_length(), 3);
228+
assert_eq!(Big::from_small(0x18).bit_length(), 5);
229+
assert_eq!(Big::from_u64(0x4073).bit_length(), 15);
230+
assert_eq!(Big::from_u64(0xffffff).bit_length(), 24);
231+
}
232+
144233
#[test]
145234
fn test_ord() {
146235
assert!(Big::from_u64(0) < Big::from_u64(0xffffff));

0 commit comments

Comments
 (0)