about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-08 20:19:00 +0000
committerbors <bors@rust-lang.org>2021-07-08 20:19:00 +0000
commit8b87e85394aa583b01e53aef06343dd0749a3324 (patch)
tree8268032841856f0ae88c992936e8b2992832e417
parentaa65b08b1dbaf4b637847646801ebc8c01d7ecbd (diff)
parented76c11202c66154a5779f45a9ac70eeccdc9d8b (diff)
downloadrust-8b87e85394aa583b01e53aef06343dd0749a3324.tar.gz
rust-8b87e85394aa583b01e53aef06343dd0749a3324.zip
Auto merge of #86930 - tspiteri:int_log10, r=kennytm
special case for integer log10

Now that #80918 has been merged, this PR provides a faster version of `log10`.

The PR also adds some tests for values close to all powers of 10.
-rw-r--r--library/core/src/num/int_log10.rs134
-rw-r--r--library/core/src/num/int_macros.rs5
-rw-r--r--library/core/src/num/mod.rs1
-rw-r--r--library/core/src/num/uint_macros.rs7
-rw-r--r--library/core/tests/num/int_log.rs54
5 files changed, 198 insertions, 3 deletions
diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs
new file mode 100644
index 00000000000..a23ca51ef87
--- /dev/null
+++ b/library/core/src/num/int_log10.rs
@@ -0,0 +1,134 @@
+mod unchecked {
+    // 0 < val <= u8::MAX
+    pub const fn u8(val: u8) -> u32 {
+        if val >= 100 {
+            2
+        } else if val >= 10 {
+            1
+        } else {
+            0
+        }
+    }
+
+    // 0 < val <= u16::MAX
+    pub const fn u16(val: u16) -> u32 {
+        if val >= 10_000 {
+            4
+        } else if val >= 1000 {
+            3
+        } else if val >= 100 {
+            2
+        } else if val >= 10 {
+            1
+        } else {
+            0
+        }
+    }
+
+    // 0 < val < 100_000_000
+    const fn less_than_8(mut val: u32) -> u32 {
+        let mut log = 0;
+        if val >= 10_000 {
+            val /= 10_000;
+            log += 4;
+        }
+        log + if val >= 1000 {
+            3
+        } else if val >= 100 {
+            2
+        } else if val >= 10 {
+            1
+        } else {
+            0
+        }
+    }
+
+    // 0 < val <= u32::MAX
+    pub const fn u32(mut val: u32) -> u32 {
+        let mut log = 0;
+        if val >= 100_000_000 {
+            val /= 100_000_000;
+            log += 8;
+        }
+        log + less_than_8(val)
+    }
+
+    // 0 < val < 10_000_000_000_000_000
+    const fn less_than_16(mut val: u64) -> u32 {
+        let mut log = 0;
+        if val >= 100_000_000 {
+            val /= 100_000_000;
+            log += 8;
+        }
+        log + less_than_8(val as u32)
+    }
+
+    // 0 < val <= u64::MAX
+    pub const fn u64(mut val: u64) -> u32 {
+        let mut log = 0;
+        if val >= 10_000_000_000_000_000 {
+            val /= 10_000_000_000_000_000;
+            log += 16;
+        }
+        log + less_than_16(val)
+    }
+
+    // 0 < val <= u128::MAX
+    pub const fn u128(mut val: u128) -> u32 {
+        let mut log = 0;
+        if val >= 100_000_000_000_000_000_000_000_000_000_000 {
+            val /= 100_000_000_000_000_000_000_000_000_000_000;
+            log += 32;
+            return log + less_than_8(val as u32);
+        }
+        if val >= 10_000_000_000_000_000 {
+            val /= 10_000_000_000_000_000;
+            log += 16;
+        }
+        log + less_than_16(val as u64)
+    }
+
+    // 0 < val <= i8::MAX
+    pub const fn i8(val: i8) -> u32 {
+        u8(val as u8)
+    }
+
+    // 0 < val <= i16::MAX
+    pub const fn i16(val: i16) -> u32 {
+        u16(val as u16)
+    }
+
+    // 0 < val <= i32::MAX
+    pub const fn i32(val: i32) -> u32 {
+        u32(val as u32)
+    }
+
+    // 0 < val <= i64::MAX
+    pub const fn i64(val: i64) -> u32 {
+        u64(val as u64)
+    }
+
+    // 0 < val <= i128::MAX
+    pub const fn i128(val: i128) -> u32 {
+        u128(val as u128)
+    }
+}
+
+macro_rules! impl_checked {
+    ($T:ident) => {
+        pub const fn $T(val: $T) -> Option<$T> {
+            if val > 0 { Some(unchecked::$T(val) as $T) } else { None }
+        }
+    };
+}
+
+impl_checked! { u8 }
+impl_checked! { u16 }
+impl_checked! { u32 }
+impl_checked! { u64 }
+impl_checked! { u128 }
+impl_checked! { i8 }
+impl_checked! { i16 }
+impl_checked! { i32 }
+impl_checked! { i64 }
+impl_checked! { i128 }
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index a9461649d4a..982729388c8 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -1929,7 +1929,10 @@ macro_rules! int_impl {
                         without modifying the original"]
         #[inline]
         pub const fn checked_log10(self) -> Option<Self> {
-            self.checked_log(10)
+            match int_log10::$ActualT(self as $ActualT) {
+                Some(s) => Some(s as Self),
+                None => None,
+            }
         }
 
         /// Computes the absolute value of `self`.
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 81d00c281ad..26d84a60702 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -41,6 +41,7 @@ mod int_macros; // import int_impl!
 mod uint_macros; // import uint_impl!
 
 mod error;
+mod int_log10;
 mod nonzero;
 mod wrapping;
 
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index bf4d2e7433e..ca1b05fdfbe 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -1,5 +1,5 @@
 macro_rules! uint_impl {
-    ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr,
+    ($SelfT:ty, $ActualT:ident, $BITS:expr, $MaxV:expr,
         $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr,
         $reversed:expr, $le_bytes:expr, $be_bytes:expr,
         $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => {
@@ -819,7 +819,10 @@ macro_rules! uint_impl {
                         without modifying the original"]
         #[inline]
         pub const fn checked_log10(self) -> Option<Self> {
-            self.checked_log(10)
+            match int_log10::$ActualT(self as $ActualT) {
+                Some(s) => Some(s as Self),
+                None => None,
+            }
         }
 
         /// Checked negation. Computes `-self`, returning `None` unless `self ==
diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs
index 99a9b17ab11..51122c11ce1 100644
--- a/library/core/tests/num/int_log.rs
+++ b/library/core/tests/num/int_log.rs
@@ -97,3 +97,57 @@ fn checked_log10() {
         assert_eq!(i.checked_log10(), Some((i as f32).log10() as u16));
     }
 }
+
+macro_rules! log10_loop {
+    ($T:ty, $log10_max:expr) => {
+        assert_eq!(<$T>::MAX.log10(), $log10_max);
+        for i in 0..=$log10_max {
+            let p = (10 as $T).pow(i as u32);
+            if p >= 10 {
+                assert_eq!((p - 9).log10(), i - 1);
+                assert_eq!((p - 1).log10(), i - 1);
+            }
+            assert_eq!(p.log10(), i);
+            assert_eq!((p + 1).log10(), i);
+            if p >= 10 {
+                assert_eq!((p + 9).log10(), i);
+            }
+
+            // also check `x.log(10)`
+            if p >= 10 {
+                assert_eq!((p - 9).log(10), i - 1);
+                assert_eq!((p - 1).log(10), i - 1);
+            }
+            assert_eq!(p.log(10), i);
+            assert_eq!((p + 1).log(10), i);
+            if p >= 10 {
+                assert_eq!((p + 9).log(10), i);
+            }
+        }
+    };
+}
+
+#[test]
+fn log10_u8() {
+    log10_loop! { u8, 2 }
+}
+
+#[test]
+fn log10_u16() {
+    log10_loop! { u16, 4 }
+}
+
+#[test]
+fn log10_u32() {
+    log10_loop! { u32, 9 }
+}
+
+#[test]
+fn log10_u64() {
+    log10_loop! { u64, 19 }
+}
+
+#[test]
+fn log10_u128() {
+    log10_loop! { u128, 38 }
+}