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.
This commit is contained in:
Christopher Swenson 2022-01-10 14:18:28 -08:00
parent 89b9f7b284
commit 424f38f211
2 changed files with 38 additions and 10 deletions

View File

@ -162,20 +162,13 @@ macro_rules! define_bignum {
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() {
if end == 0 {
// 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;
}
i + 1
let end_leading_zeros = digits[end - 1].leading_zeros() as usize;
end * digitbits - end_leading_zeros
}
/// Adds `other` to itself and returns its own mutable reference.

View File

@ -1,4 +1,5 @@
use core::num::bignum::tests::Big8x3 as Big;
use core::num::bignum::Big32x40;
#[test]
#[should_panic]
@ -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);
@ -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));