diff --git a/library/core/benches/num/int_log/mod.rs b/library/core/benches/num/int_log/mod.rs index bb61224b5ba..3807cd5d76c 100644 --- a/library/core/benches/num/int_log/mod.rs +++ b/library/core/benches/num/int_log/mod.rs @@ -1,7 +1,7 @@ use rand::Rng; use test::{black_box, Bencher}; -macro_rules! int_log_bench { +macro_rules! int_log10_bench { ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => { #[bench] fn $predictable(bench: &mut Bencher) { @@ -51,8 +51,75 @@ fn $random_small(bench: &mut Bencher) { }; } -int_log_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} -int_log_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} -int_log_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} -int_log_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} -int_log_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} +int_log10_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} +int_log10_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} +int_log10_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} +int_log10_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} +int_log10_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} + +macro_rules! int_log_bench { + ($t:ty, $random:ident, $random_small:ident, $geometric:ident) => { + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS); + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t; + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $geometric(bench: &mut Bencher) { + let bases: [$t; 16] = [2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]; + let base_and_numbers: Vec<($t, Vec<$t>)> = bases + .iter() + .map(|&b| { + let numbers = (0..=<$t>::MAX.ilog(b)).map(|exp| b.pow(exp)).collect(); + (b, numbers) + }) + .collect(); + bench.iter(|| { + for (b, numbers) in &base_and_numbers { + for &x in numbers { + black_box(black_box(x).ilog(black_box(*b))); + } + } + }); + } + }; +} + +int_log_bench! {u8, u8_log_random, u8_log_random_small, u8_log_geometric} +int_log_bench! {u16, u16_log_random, u16_log_random_small, u16_log_geometric} +int_log_bench! {u32, u32_log_random, u32_log_random_small, u32_log_geometric} +int_log_bench! {u64, u64_log_random, u64_log_random_small, u64_log_geometric} +int_log_bench! {u128, u128_log_random, u128_log_random_small, u128_log_geometric} diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index de25b999bde..a92da88dd3f 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3123,21 +3123,9 @@ pub const fn checked_ilog(self, base: Self) -> Option { if self <= 0 || base <= 1 { None } else { - let mut n = 0; - let mut r = self; - - // Optimization for 128 bit wide integers. - if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r /= base.pow(b as u32); - } - - while r >= base { - r /= base; - n += 1; - } - Some(n) + // Delegate to the unsigned implementation. + // The condition makes sure that both casts are exact. + (self as $UnsignedT).checked_ilog(base as $UnsignedT) } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 58f713baa57..c13763243f0 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1095,18 +1095,25 @@ pub const fn checked_ilog(self, base: Self) -> Option { None } else { let mut n = 0; - let mut r = self; + let mut r = 1; // Optimization for 128 bit wide integers. if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r /= base.pow(b as u32); + // The following is a correct lower bound for ⌊log(base,self)⌋ because + // + // log(base,self) = log(2,self) / log(2,base) + // ≥ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) + // + // hence + // + // ⌊log(base,self)⌋ ≥ ⌊ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) ⌋ . + n = self.ilog2() / (base.ilog2() + 1); + r = base.pow(n); } - while r >= base { - r /= base; + while r <= self / base { n += 1; + r *= base; } Some(n) }