diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs index e4599067f85..d28552cf4aa 100644 --- a/library/core/src/num/int_log10.rs +++ b/library/core/src/num/int_log10.rs @@ -1,76 +1,71 @@ mod unchecked { // 0 < val <= u8::MAX pub const fn u8(val: u8) -> u32 { - if val >= 100 { - 2 - } else if val >= 10 { - 1 - } else { - 0 - } + let val = val as u32; + + // For better performance, avoid branches by assembling the solution + // in the bits above the low 8 bits. + + // Adding c1 to val gives 10 in the top bits for val < 10, 11 for val >= 10 + let c1 = 0b11_00000000 - 10; // 758 + // Adding c2 to val gives 01 in the top bits for val < 100, 10 for val >= 100 + let c2 = 0b10_00000000 - 100; // 412 + + // Value of top bits: + // +c1 +c2 1&2 + // 0..=9 10 01 00 = 0 + // 10..=99 11 01 01 = 1 + // 100..=255 11 10 10 = 2 + ((val + c1) & (val + c2)) >> 8 + } + + // 0 < val < 100_000 + const fn less_than_5(val: u32) -> u32 { + // Similar to u8, when adding one of these constants to val, + // we get two possible bit patterns above the low 17 bits, + // depending on whether val is below or above the threshold. + let c1 = 0b011_00000000000000000 - 10; // 393206 + let c2 = 0b100_00000000000000000 - 100; // 524188 + let c3 = 0b111_00000000000000000 - 1000; // 916504 + let c4 = 0b100_00000000000000000 - 10000; // 514288 + + // Value of top bits: + // +c1 +c2 1&2 +c3 +c4 3&4 ^ + // 0..=9 010 011 010 110 011 010 000 = 0 + // 10..=99 011 011 011 110 011 010 001 = 1 + // 100..=999 011 100 000 110 011 010 010 = 2 + // 1000..=9999 011 100 000 111 011 011 011 = 3 + // 10000..=99999 011 100 000 111 100 100 100 = 4 + (((val + c1) & (val + c2)) ^ ((val + c3) & (val + c4))) >> 17 } // 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 - } + less_than_5(val as u32) } // 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; + if val >= 100_000 { + val /= 100_000; + log += 5; } - 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) + log + less_than_5(val) } // 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; + if val >= 10_000_000_000 { + val /= 10_000_000_000; + log += 10; } - log + less_than_16(val) + if val >= 100_000 { + val /= 100_000; + log += 5; + } + log + less_than_5(val as u32) } // 0 < val <= u128::MAX @@ -79,13 +74,13 @@ mod unchecked { 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); + return log + u32(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) + log + u64(val as u64) } // 0 < val <= i8::MAX diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs index 1517e8a952f..3cd0073ddd8 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/core/tests/num/int_log.rs @@ -96,6 +96,9 @@ fn checked_log10() { for i in 1..=u16::MAX { assert_eq!(i.checked_log10(), Some((i as f32).log10() as u32)); } + for i in 1..=100_000u32 { + assert_eq!(i.checked_log10(), Some((i as f32).log10() as u32)); + } } macro_rules! log10_loop {