# Number of Digits in a Number

·For reasons involving string-formatting and width adjustments in a project, I ran into the pickle of figuring out how to get the number of digits in a number.

This isn’t a serious problem.
In theory I *could* have just used `foobar.digits.size`

,
be done with it, and save the hassle of writing this post.
While premature optimization is said to be the root of all evil,
this in particular felt more like a challenge to figure out how to
get the number of digits in a number **without going through a loop**.
Constant time rather than linear just to get this info is a bonus.

Since this project is written in Crystal, so is the code here.

# Traditional Way

Like before, `foobar.digits.size`

would be the easy way out.
`Int#digits`

just gives us an `Array`

of digits in
a number by looping, dividing and getting the remainder from 10
and stopping when the quotient reaches zero.

```
# Int#digits is part of the standard library in Crystal
# but this method illustrates the gist of this.
def digits(number : Int32)
digits = Array(Int32).new
while !number.zero?
digits.push (number % 10).to_i
number = number.tdiv 10
end
return digits
end
```

The amount of iterations in this kind of loop is never large.
A `UInt32`

’s maximum limit is ten digits and a `UInt64`

’s,
the largest fixed number type, twenty digits.

# The `rocx`

-y Road

For some reason this situation brought up “powers of ten” in my head.
Specifically the part where, say, 10^{3} means there’s three
zeroes in 1,000.
What’s the inverse of doing that?
The logarithm.
That looks like a good place to start.

```
Math.log10(2024)
# ⇒ 3.3062105081677613 : Float64
Math.log10(30)
# ⇒ 1.4771212547196624 : Float64
# Am I on the right track? 🤔 Let's try this...
Math.log10(2024).ceil.to_i
# ⇒ 4 : Int32
Math.log10(30)
# ⇒ 2 : Int32
# 😎
```

Nice. But there’s one snag I encountered while testing: dealing with powers of ten (or whatever the base used is).

```
Math.log10(1000).ceil.to_i
# ⇒ 3 : Int32
#
```

Well drat.
Time to pull up a playground and tinker around.
There *has* to be a pattern *somewhere*…

```
log_999 = Math.log10 999
# ⇒ 2.9995654882259823 : Float64
log_1000 = Math.log10 1000
# ⇒ 3.0 : Float64
log_1001 = Math.log10 1001
# ⇒ 3.000434077479319 : Float64
log_999.ceil # ⇒ 3.0 : Float64
log_1000.ceil # ⇒ 3.0 : Float64
log_1001.ceil # ⇒ 4.0 : Float64
```

*Ooooooh I see now…*

# Aha! A Solution!

There’s the pattern.
It looks like it’s a typical off-by-one error in my logic.
What needs to be done is to get the logarithm of a given
number *plus one*, turning log_{10}(1000)
into log_{10}(1000 + 1).

```
def digit_count(number : Int32) : Int32
return Math.log10(number.succ).ceil.to_i
end
digit_count 999 # ⇒ 3 : Int32
digit_count 1000 # ⇒ 4 : Int32
digit_count 1001 # ⇒ 4 : Int32
```

No loops. Just raw mathematics. You never think this kind of stuff comes up outside of a problem domain in computer science and yet it still comes in handy.