Skip to content

Commit f511360

Browse files
authored
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.
2 parents 38c22af + 0589cac commit f511360

File tree

2 files changed

+41
-15
lines changed

2 files changed

+41
-15
lines changed

library/core/src/num/bignum.rs

+6-15
Original file line numberDiff line numberDiff line change
@@ -158,24 +158,15 @@ macro_rules! define_bignum {
158158
/// Returns the number of bits necessary to represent this value. Note that zero
159159
/// is considered to need 0 bits.
160160
pub fn bit_length(&self) -> usize {
161-
// Skip over the most significant digits which are zero.
161+
let digitbits = <$ty>::BITS as usize;
162162
let digits = self.digits();
163-
let zeros = digits.iter().rev().take_while(|&&x| x == 0).count();
164-
let end = digits.len() - zeros;
165-
let nonzero = &digits[..end];
166-
167-
if nonzero.is_empty() {
163+
// Find the most significant non-zero digit.
164+
let msd = digits.iter().rposition(|&x| x != 0);
165+
match msd {
166+
Some(msd) => msd * digitbits + digits[msd].log2() as usize + 1,
168167
// There are no non-zero digits, i.e., the number is zero.
169-
return 0;
170-
}
171-
// This could be optimized with leading_zeros() and bit shifts, but that's
172-
// probably not worth the hassle.
173-
let digitbits = <$ty>::BITS as usize;
174-
let mut i = nonzero.len() * digitbits - 1;
175-
while self.get_bit(i) == 0 {
176-
i -= 1;
168+
_ => 0,
177169
}
178-
i + 1
179170
}
180171

181172
/// Adds `other` to itself and returns its own mutable reference.

library/core/tests/num/bignum.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::num::bignum::tests::Big8x3 as Big;
2+
use core::num::bignum::Big32x40;
23

34
#[test]
45
#[should_panic]
@@ -215,6 +216,16 @@ fn test_get_bit_out_of_range() {
215216

216217
#[test]
217218
fn test_bit_length() {
219+
for i in 0..8 * 3 {
220+
// 010000...000
221+
assert_eq!(Big::from_small(1).mul_pow2(i).bit_length(), i + 1);
222+
}
223+
for i in 1..8 * 3 - 1 {
224+
// 010000...001
225+
assert_eq!(Big::from_small(1).mul_pow2(i).add(&Big::from_small(1)).bit_length(), i + 1);
226+
// 110000...000
227+
assert_eq!(Big::from_small(3).mul_pow2(i).bit_length(), i + 2);
228+
}
218229
assert_eq!(Big::from_small(0).bit_length(), 0);
219230
assert_eq!(Big::from_small(1).bit_length(), 1);
220231
assert_eq!(Big::from_small(5).bit_length(), 3);
@@ -223,6 +234,30 @@ fn test_bit_length() {
223234
assert_eq!(Big::from_u64(0xffffff).bit_length(), 24);
224235
}
225236

237+
#[test]
238+
fn test_bit_length_32x40() {
239+
for i in 0..32 * 40 {
240+
// 010000...000
241+
assert_eq!(Big32x40::from_small(1).mul_pow2(i).bit_length(), i + 1);
242+
}
243+
for i in 1..32 * 40 - 1 {
244+
// 010000...001
245+
assert_eq!(
246+
Big32x40::from_small(1).mul_pow2(i).add(&Big32x40::from_small(1)).bit_length(),
247+
i + 1
248+
);
249+
// 110000...000
250+
assert_eq!(Big32x40::from_small(3).mul_pow2(i).bit_length(), i + 2);
251+
}
252+
assert_eq!(Big32x40::from_small(0).bit_length(), 0);
253+
assert_eq!(Big32x40::from_small(1).bit_length(), 1);
254+
assert_eq!(Big32x40::from_small(5).bit_length(), 3);
255+
assert_eq!(Big32x40::from_small(0x18).bit_length(), 5);
256+
assert_eq!(Big32x40::from_u64(0x4073).bit_length(), 15);
257+
assert_eq!(Big32x40::from_u64(0xffffff).bit_length(), 24);
258+
assert_eq!(Big32x40::from_u64(0xffffffffffffffff).bit_length(), 64);
259+
}
260+
226261
#[test]
227262
fn test_ord() {
228263
assert!(Big::from_u64(0) < Big::from_u64(0xffffff));

0 commit comments

Comments
 (0)