]> git.lizzy.rs Git - rust.git/commitdiff
Simplification of BigNum::bit_length
authorChristopher Swenson <swenson@swenson.io>
Mon, 10 Jan 2022 22:18:28 +0000 (14:18 -0800)
committerChristopher Swenson <swenson@swenson.io>
Mon, 10 Jan 2022 22:18:28 +0000 (14:18 -0800)
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.

library/core/src/num/bignum.rs
library/core/tests/num/bignum.rs

index 8a06a0988829b646d871433c1986291875abfe55..65c1753895e7162928eaa7e449e001d33a975d14 100644 (file)
@@ -162,20 +162,13 @@ pub fn bit_length(&self) -> 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() {
+                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.
index 1457064cc8d572028374f5e95fda860a605dce53..416e7cea7a67bcf040206dbcc777f1d6a6838aeb 100644 (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));