diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs new file mode 100644 index 0000000000000..a23ca51ef87d0 --- /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 a9461649d4aa1..982729388c87d 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.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 81d00c281ade8..26d84a60702c5 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 bf4d2e7433e2f..ca1b05fdfbe48 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.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 99a9b17ab1183..51122c11ce1e2 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 } +}