diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2022-01-15 11:28:22 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-01-15 11:28:22 +0100 |
| commit | f511360fd29e426c4a24ebde038f5bfbfcf93f88 (patch) | |
| tree | bdb41b553f0998d6a77ff21c7877a7546477b74b | |
| parent | 38c22af0153cf8f920c01ef04493e8878401fd18 (diff) | |
| parent | 0589cace8c943d40a60b7356d8c772baf2879cee (diff) | |
| download | rust-f511360fd29e426c4a24ebde038f5bfbfcf93f88.tar.gz rust-f511360fd29e426c4a24ebde038f5bfbfcf93f88.zip | |
Rollup merge of #92747 - swenson:bignum-bit-length-optimization, r=scottmcm
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.
| -rw-r--r-- | library/core/src/num/bignum.rs | 21 | ||||
| -rw-r--r-- | library/core/tests/num/bignum.rs | 35 |
2 files changed, 41 insertions, 15 deletions
diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index 8a06a098882..98d8a8a1d74 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -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. diff --git a/library/core/tests/num/bignum.rs b/library/core/tests/num/bignum.rs index 1457064cc8d..416e7cea7a6 100644 --- a/library/core/tests/num/bignum.rs +++ b/library/core/tests/num/bignum.rs @@ -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); @@ -224,6 +235,30 @@ fn test_bit_length() { } #[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)); assert!(Big::from_u64(0x102) < Big::from_u64(0x201)); |
