AthulMuralidhar
AthulMuralidhar

Reputation: 715

Getting the length of an int

I am trying to get the length (the number of digits when interpreted in decimal) of an int in rust. I found a way to do it, however am looking for method that comes from the primitive itself. This is what I have:

let num = 90.to_string();
println!("num: {}", num.chars().count())
// num: 2

I am looking at https://docs.rs/digits/0.3.3/digits/struct.Digits.html#method.length. is this a good candidate? How do I use it? Or are there other crates that does it for me?

One liners with less type conversion is the ideal solution I am looking for.

Upvotes: 8

Views: 10763

Answers (6)

Daniel
Daniel

Reputation: 16464

You could loop and check how often you can divide the number by 10 before it becomes a single digit. Or in the other direction (because division is slower than multiplication), check how often you can multiply 10*10*...*10 until you reach the number:

fn length(n: u32, base: u32) -> u32 {
    let mut power = base;
    let mut count = 1;
    while n >= power {
        count += 1;
        if let Some(new_power) = power.checked_mul(base) {
            power = new_power;
        } else {
            break;
        }
    }
    count
}

Since rust 1.67, you can use:

n.checked_ilog10().unwrap_or(0) + 1

Upvotes: 15

Franci
Franci

Reputation: 2245

if num is signed:

let digits = (num.abs() as f64 + 0.1).log10().ceil() as u32;

Upvotes: 1

Todd
Todd

Reputation: 5405

The first method below relies on the following formula, where a and b are the logarithmic bases.

log<a>( x ) = log<b>( x ) / log<b>( a )

log<a>( x ) = log<2>( x ) / log<2>( a )  // Substituting 2 for `b`.

The following function can be applied to finding the number of digits for bases that are a power of 2. This approach is very fast.

fn num_digits_base_pow2(n: u64, b: u32) -> u32
{
    (63 - n.leading_zeros()) / (31 - b.leading_zeros()) + 1
}

The bits are counted for both n (the number we want to represent) and b (the base) to find their log2 floor values. Then the adjusted ratio of these values gives the ceiling log value in the desired base.

For a general purpose approach to finding the number of digits for arbitrary bases, the following should suffice.

fn num_digits(n: u64, b: u32) -> u32
{
    (n as f64).log(b as f64).ceil() as u32
}

Upvotes: 1

user4815162342
user4815162342

Reputation: 155276

Here is a one-liner that doesn't require strings or floating point:

println!("num: {}", successors(Some(n), |&n| (n >= 10).then(|| n / 10)).count());

It simply counts the number of times the initial number needs to be divided by 10 in order to reach 0.


EDIT: the first version of this answer used iterate from the (excellent and highly recommended) itertools crate, but @trentcl pointed out that successors from the stdlib does the same. For reference, here is the version using iterate:

println!("num: {}", iterate(n, |&n| n / 10).take_while(|&n| n > 0).count().max(1));

Upvotes: 5

Bromind
Bromind

Reputation: 1138

A nice property of numbers that is always good to have in mind is that the number of digits required to write a number $x$ in base $n$ is actually $\lceil log_n(x + 1) \rceil$.

Therefore, one can simply write the following function (notice the cast from u32 to f32, since integers don't have a log function).

fn length(n: u32, base: u32) -> u32 {
    let n = (n+1) as f32;
    n.log(base as f32).ceil() as u32
}

You can easily adapt it for negative numbers. For floating point numbers this might be a bit (i.e. a lot) more tricky.

To take into account Daniel's comment about the pathological cases introduced by using f32, note that, with nightly Rust, integers have a logarithm method. (Notice that, imo, those are implementation details, and you should more focus on understanding the algorithm than the implementation.):

#![feature(int_log)]

fn length(n: u32, base: u32) -> u32 {
    n.log(base) + 1
}

Upvotes: -4

weltensturm
weltensturm

Reputation: 2162

Here's a (barely) one-liner that's faster than doing a string conversion, using std::iter stuff:

let some_int = 9834;
let decimal_places = (0..).take_while(|i| 10u64.pow(*i) <= some_int).count();

Upvotes: 1

Related Questions