Skip to content

Commit

Permalink
Rollup merge of #92747 - swenson:bignum-bit-length-optimization, r=sc…
Browse files Browse the repository at this point in the history
…ottmcm

Simplification of BigNum::bit_length

As indicated in the comment, the BigNum::bit_length function could be
optimized by using CLZ, which is often a single instruction instead a
loop.

I think the code is also simpler now without the loop.

I added some additional tests for Big8x3 and Big32x40 to ensure that
there were no regressions.
  • Loading branch information
matthiaskrgr authored Jan 15, 2022
2 parents 38c22af + 0589cac commit f511360
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 15 deletions.
21 changes: 6 additions & 15 deletions library/core/src/num/bignum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,24 +158,15 @@ macro_rules! define_bignum {
/// 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 {
// Skip over the most significant digits which are zero.
let digitbits = <$ty>::BITS as usize;
let digits = self.digits();
let zeros = digits.iter().rev().take_while(|&&x| x == 0).count();
let end = digits.len() - zeros;
let nonzero = &digits[..end];

if nonzero.is_empty() {
// Find the most significant non-zero digit.
let msd = digits.iter().rposition(|&x| x != 0);
match msd {
Some(msd) => msd * digitbits + digits[msd].log2() as usize + 1,
// 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 digitbits = <$ty>::BITS as usize;
let mut i = nonzero.len() * digitbits - 1;
while self.get_bit(i) == 0 {
i -= 1;
_ => 0,
}
i + 1
}

/// Adds `other` to itself and returns its own mutable reference.
Expand Down
35 changes: 35 additions & 0 deletions library/core/tests/num/bignum.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::num::bignum::tests::Big8x3 as Big;
use core::num::bignum::Big32x40;

#[test]
#[should_panic]
Expand Down Expand Up @@ -215,6 +216,16 @@ fn test_get_bit_out_of_range() {

#[test]
fn test_bit_length() {
for i in 0..8 * 3 {
// 010000...000
assert_eq!(Big::from_small(1).mul_pow2(i).bit_length(), i + 1);
}
for i in 1..8 * 3 - 1 {
// 010000...001
assert_eq!(Big::from_small(1).mul_pow2(i).add(&Big::from_small(1)).bit_length(), i + 1);
// 110000...000
assert_eq!(Big::from_small(3).mul_pow2(i).bit_length(), i + 2);
}
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);
Expand All @@ -223,6 +234,30 @@ fn test_bit_length() {
assert_eq!(Big::from_u64(0xffffff).bit_length(), 24);
}

#[test]
fn test_bit_length_32x40() {
for i in 0..32 * 40 {
// 010000...000
assert_eq!(Big32x40::from_small(1).mul_pow2(i).bit_length(), i + 1);
}
for i in 1..32 * 40 - 1 {
// 010000...001
assert_eq!(
Big32x40::from_small(1).mul_pow2(i).add(&Big32x40::from_small(1)).bit_length(),
i + 1
);
// 110000...000
assert_eq!(Big32x40::from_small(3).mul_pow2(i).bit_length(), i + 2);
}
assert_eq!(Big32x40::from_small(0).bit_length(), 0);
assert_eq!(Big32x40::from_small(1).bit_length(), 1);
assert_eq!(Big32x40::from_small(5).bit_length(), 3);
assert_eq!(Big32x40::from_small(0x18).bit_length(), 5);
assert_eq!(Big32x40::from_u64(0x4073).bit_length(), 15);
assert_eq!(Big32x40::from_u64(0xffffff).bit_length(), 24);
assert_eq!(Big32x40::from_u64(0xffffffffffffffff).bit_length(), 64);
}

#[test]
fn test_ord() {
assert!(Big::from_u64(0) < Big::from_u64(0xffffff));
Expand Down

0 comments on commit f511360

Please sign in to comment.