Skip to content

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

License

Notifications You must be signed in to change notification settings

nordzilla/count-digits

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

82 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

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

Note

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

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