Skip to content

Commit 8b87e85

Browse files
committed
Auto merge of rust-lang#86930 - tspiteri:int_log10, r=kennytm
special case for integer log10 Now that rust-lang#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.
2 parents aa65b08 + ed76c11 commit 8b87e85

File tree

5 files changed

+198
-3
lines changed

5 files changed

+198
-3
lines changed

library/core/src/num/int_log10.rs

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
mod unchecked {
2+
// 0 < val <= u8::MAX
3+
pub const fn u8(val: u8) -> u32 {
4+
if val >= 100 {
5+
2
6+
} else if val >= 10 {
7+
1
8+
} else {
9+
0
10+
}
11+
}
12+
13+
// 0 < val <= u16::MAX
14+
pub const fn u16(val: u16) -> u32 {
15+
if val >= 10_000 {
16+
4
17+
} else if val >= 1000 {
18+
3
19+
} else if val >= 100 {
20+
2
21+
} else if val >= 10 {
22+
1
23+
} else {
24+
0
25+
}
26+
}
27+
28+
// 0 < val < 100_000_000
29+
const fn less_than_8(mut val: u32) -> u32 {
30+
let mut log = 0;
31+
if val >= 10_000 {
32+
val /= 10_000;
33+
log += 4;
34+
}
35+
log + if val >= 1000 {
36+
3
37+
} else if val >= 100 {
38+
2
39+
} else if val >= 10 {
40+
1
41+
} else {
42+
0
43+
}
44+
}
45+
46+
// 0 < val <= u32::MAX
47+
pub const fn u32(mut val: u32) -> u32 {
48+
let mut log = 0;
49+
if val >= 100_000_000 {
50+
val /= 100_000_000;
51+
log += 8;
52+
}
53+
log + less_than_8(val)
54+
}
55+
56+
// 0 < val < 10_000_000_000_000_000
57+
const fn less_than_16(mut val: u64) -> u32 {
58+
let mut log = 0;
59+
if val >= 100_000_000 {
60+
val /= 100_000_000;
61+
log += 8;
62+
}
63+
log + less_than_8(val as u32)
64+
}
65+
66+
// 0 < val <= u64::MAX
67+
pub const fn u64(mut val: u64) -> u32 {
68+
let mut log = 0;
69+
if val >= 10_000_000_000_000_000 {
70+
val /= 10_000_000_000_000_000;
71+
log += 16;
72+
}
73+
log + less_than_16(val)
74+
}
75+
76+
// 0 < val <= u128::MAX
77+
pub const fn u128(mut val: u128) -> u32 {
78+
let mut log = 0;
79+
if val >= 100_000_000_000_000_000_000_000_000_000_000 {
80+
val /= 100_000_000_000_000_000_000_000_000_000_000;
81+
log += 32;
82+
return log + less_than_8(val as u32);
83+
}
84+
if val >= 10_000_000_000_000_000 {
85+
val /= 10_000_000_000_000_000;
86+
log += 16;
87+
}
88+
log + less_than_16(val as u64)
89+
}
90+
91+
// 0 < val <= i8::MAX
92+
pub const fn i8(val: i8) -> u32 {
93+
u8(val as u8)
94+
}
95+
96+
// 0 < val <= i16::MAX
97+
pub const fn i16(val: i16) -> u32 {
98+
u16(val as u16)
99+
}
100+
101+
// 0 < val <= i32::MAX
102+
pub const fn i32(val: i32) -> u32 {
103+
u32(val as u32)
104+
}
105+
106+
// 0 < val <= i64::MAX
107+
pub const fn i64(val: i64) -> u32 {
108+
u64(val as u64)
109+
}
110+
111+
// 0 < val <= i128::MAX
112+
pub const fn i128(val: i128) -> u32 {
113+
u128(val as u128)
114+
}
115+
}
116+
117+
macro_rules! impl_checked {
118+
($T:ident) => {
119+
pub const fn $T(val: $T) -> Option<$T> {
120+
if val > 0 { Some(unchecked::$T(val) as $T) } else { None }
121+
}
122+
};
123+
}
124+
125+
impl_checked! { u8 }
126+
impl_checked! { u16 }
127+
impl_checked! { u32 }
128+
impl_checked! { u64 }
129+
impl_checked! { u128 }
130+
impl_checked! { i8 }
131+
impl_checked! { i16 }
132+
impl_checked! { i32 }
133+
impl_checked! { i64 }
134+
impl_checked! { i128 }

library/core/src/num/int_macros.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1929,7 +1929,10 @@ macro_rules! int_impl {
19291929
without modifying the original"]
19301930
#[inline]
19311931
pub const fn checked_log10(self) -> Option<Self> {
1932-
self.checked_log(10)
1932+
match int_log10::$ActualT(self as $ActualT) {
1933+
Some(s) => Some(s as Self),
1934+
None => None,
1935+
}
19331936
}
19341937

19351938
/// Computes the absolute value of `self`.

library/core/src/num/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ mod int_macros; // import int_impl!
4141
mod uint_macros; // import uint_impl!
4242

4343
mod error;
44+
mod int_log10;
4445
mod nonzero;
4546
mod wrapping;
4647

library/core/src/num/uint_macros.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
macro_rules! uint_impl {
2-
($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr,
2+
($SelfT:ty, $ActualT:ident, $BITS:expr, $MaxV:expr,
33
$rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr,
44
$reversed:expr, $le_bytes:expr, $be_bytes:expr,
55
$to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => {
@@ -819,7 +819,10 @@ macro_rules! uint_impl {
819819
without modifying the original"]
820820
#[inline]
821821
pub const fn checked_log10(self) -> Option<Self> {
822-
self.checked_log(10)
822+
match int_log10::$ActualT(self as $ActualT) {
823+
Some(s) => Some(s as Self),
824+
None => None,
825+
}
823826
}
824827

825828
/// Checked negation. Computes `-self`, returning `None` unless `self ==

library/core/tests/num/int_log.rs

+54
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,57 @@ fn checked_log10() {
9797
assert_eq!(i.checked_log10(), Some((i as f32).log10() as u16));
9898
}
9999
}
100+
101+
macro_rules! log10_loop {
102+
($T:ty, $log10_max:expr) => {
103+
assert_eq!(<$T>::MAX.log10(), $log10_max);
104+
for i in 0..=$log10_max {
105+
let p = (10 as $T).pow(i as u32);
106+
if p >= 10 {
107+
assert_eq!((p - 9).log10(), i - 1);
108+
assert_eq!((p - 1).log10(), i - 1);
109+
}
110+
assert_eq!(p.log10(), i);
111+
assert_eq!((p + 1).log10(), i);
112+
if p >= 10 {
113+
assert_eq!((p + 9).log10(), i);
114+
}
115+
116+
// also check `x.log(10)`
117+
if p >= 10 {
118+
assert_eq!((p - 9).log(10), i - 1);
119+
assert_eq!((p - 1).log(10), i - 1);
120+
}
121+
assert_eq!(p.log(10), i);
122+
assert_eq!((p + 1).log(10), i);
123+
if p >= 10 {
124+
assert_eq!((p + 9).log(10), i);
125+
}
126+
}
127+
};
128+
}
129+
130+
#[test]
131+
fn log10_u8() {
132+
log10_loop! { u8, 2 }
133+
}
134+
135+
#[test]
136+
fn log10_u16() {
137+
log10_loop! { u16, 4 }
138+
}
139+
140+
#[test]
141+
fn log10_u32() {
142+
log10_loop! { u32, 9 }
143+
}
144+
145+
#[test]
146+
fn log10_u64() {
147+
log10_loop! { u64, 19 }
148+
}
149+
150+
#[test]
151+
fn log10_u128() {
152+
log10_loop! { u128, 38 }
153+
}

0 commit comments

Comments
 (0)