#count #digits #integer #length #trait

no-std count-digits

A no-std trait to determine the lengths of integers in various number bases

13 releases (4 breaking)

0.5.1 Feb 11, 2024
0.5.0 Feb 9, 2024
0.4.0 Feb 8, 2024
0.3.1 Feb 8, 2024
0.1.0 Jan 23, 2024

#217 in Algorithms

Download history 22/week @ 2024-01-17 137/week @ 2024-01-24 142/week @ 2024-01-31 100/week @ 2024-02-07 284/week @ 2024-02-14

685 downloads per month

MIT license

89KB
946 lines

count-digits

github crates-io docs-rs

license build codecov


CountDigits is a no-std trait with functions to determine the lengths of integers in various number bases.

It is implemented for all primitive integer types and all non-zero integer types.

pub trait CountDigits: Copy + Sized {
    /// The type of integer that should be used for radix arguments.
    type Radix;

    /// Returns the count of bits in an integer.
    fn count_bits(self) -> u32;

    /// Returns the count of octal digits in an integer.
    fn count_octal_digits(self) -> u32;

    /// Returns the count of hexadecimal digits in an integer.
    fn count_hex_digits(self) -> u32;

    /// Returns the count of decimal digits in an integer.
    fn count_digits(self) -> usize;

    /// Returns the count of digits in an integer for a given radix.
    /// Panics if the provided radix is invalid.
    fn count_digits_radix(self, radix: Self::Radix) -> usize;

    /// Returns the count of digits in an integer for a given radix.
    /// Returns None if the given radix is invalid.
    fn checked_count_digits_radix(self, radix: Self::Radix) -> Option<usize>;
}

Examples

use count_digits::CountDigits;

// Base 2
assert_eq!(16, 0b1111000000001101.count_bits());
assert_eq!(16, 0b1111000000001101.count_digits_radix(2_u32));

// Base 8
assert_eq!(06, 0o170015.count_octal_digits());
assert_eq!(06, 0o170015.count_digits_radix(8_u32));

// Base 10
assert_eq!(05, 61453.count_digits());
assert_eq!(05, 61453.count_digits_radix(10_u32));

// Base 16
assert_eq!(04, 0xF00D.count_hex_digits());
assert_eq!(04, 0xF00D.count_digits_radix(16_u32));

Functions That Return u32

Named functions for which the radix is a power of two return u32 for compatibility with Rust's bitwise functions and constants.

assert_eq!(0b1011___u8.count_bits(),   u8::BITS - 0b1011___u8.leading_zeros());
assert_eq!(0b1011__i32.count_bits(),  i32::BITS - 0b1011__i32.leading_zeros());
assert_eq!(0b1011_u128.count_bits(), u128::BITS - 0b1011_u128.leading_zeros());

Functions That Return usize

Functions that are not inherently meaningful in a bitwise context return usize for compatibility with Rust's formatting functions and macros.

let numbers = [2, 3, 13, 103, 1337];
let max_digits = numbers
    .iter()
    .map(CountDigits::count_digits)
    .max()
    .unwrap();

for n in numbers {
    assert_eq!(4, format!("{n:>max_digits$}").chars().count());
}

When formatting binary, octal, or hexadecimal numbers, the count_digits_radix(2 | 8 | 16) and checked_count_digits_radix(2 | 8 | 16) functions can be used in place of count_bits(), count_octal_digits(), and count_hex_digits() to retrieve the desired count directly as a usize.

let numbers = [0b1, 0b10, 0b101, 0b1011];
let max_bits = numbers
    .iter()
    .map(|n| n.count_digits_radix(2u32))
    .max()
    .unwrap();

for n in numbers {
    assert_eq!(4, format!("{n:>max_bits$}").chars().count());
}

Invalid Radix Values

Values passed to count_digits_radix() and checked_count_digits_radix() must be greater than or equal to 2.

for n in 0..100 {
    assert!(std::panic::catch_unwind(|| n.count_digits_radix(0_u32)).is_err());
    assert!(std::panic::catch_unwind(|| n.count_digits_radix(1_u32)).is_err());
}
for n in 0..100 {
    assert!(n.checked_count_digits_radix(0_u32).is_none());
    assert!(n.checked_count_digits_radix(1_u32).is_none());
}

Negative Numbers

Since negative numbers represented in base 10 are displayed with a negative sign, the base-10 digit count of a positive number will be equal to the base-10 digit count of the number's negated value, assuming no wrapping occurs.

Note that the negative sign itself is not included in the count because the negative sign is not a digit.

assert_eq!(
    867_5309_i32.count_digits(),
    867_5309_i32.wrapping_neg().count_digits(),
);

The digit counts of negative numbers represented in other bases reflect the twos-complement representation, and the digit count of a positive number will not be the same as the count of its negated value.

for radix in 2..=16 {
    match radix {
        10 => assert_eq!(
            0xF00D_i32.count_digits_radix(radix),
            0xF00D_i32.wrapping_neg().count_digits_radix(radix),
        ),
        _ => assert_ne!(
            0xBAD_i32.count_digits_radix(radix),
            0xBAD_i32.wrapping_neg().count_digits_radix(radix),
        ),
    }
}

This is consistent with Rust's display format.

// Base 2
assert_eq!(01, format!("{:b}",  1_i32).chars().count());
assert_eq!(32, format!("{:b}", -1_i32).chars().count());

// Base 8
assert_eq!(01, format!("{:o}",  1_i32).chars().count());
assert_eq!(11, format!("{:o}", -1_i32).chars().count());

// Base 10
assert_eq!(01, format!("{  }",  1_i32).chars().count());
assert_eq!(01, format!("{  }", -1_i32).strip_prefix('-').unwrap().chars().count());

// Base 16
assert_eq!(01, format!("{:x}",  1_i32).chars().count());
assert_eq!(08, format!("{:x}", -1_i32).chars().count());

Benchmarks

License: MIT

No runtime deps