, 3 min read
Computing the number of digits of an integer quickly
Suppose I give you an integer. How many decimal digits would you need to write it out? The number ‘100’ takes 3 digits whereas the number ’99’ requires only two.
You are effectively trying to compute the integer logarithm in base 10 of the number. I say ‘integer logarithm’ because you need to round up to the nearest integer.
Computers represent numbers in binary form, so it is easy to count the logarithm in base two. In C using GCC or clang, you can do so as follows using a counting leading zeroes function:
int int_log2(uint32_t x) { return 31 - __builtin_clz(x|1); }
Though it looks ugly, it is efficient. Most optimizing compilers on most systems will turn this into a single instruction.
How do you convert the logarithm in base 2 into the logarithm in base 10? From elementary mathematics, we have that log10 (x) = log2(x) / log2(10). So all you need is to divide by log2(10)… or get close enough. You do not want to actually divide, especially not by a floating-point value, so you want mutiply and shift instead. Multiplying and shifting is a standard technique to emulate the division.
You can get pretty close to a division by log2(10) if you multiply by 9 and then divide by 32 (2 to the power of 5). The division by a power of two is just a shift. (I initially used a division by a much larger power but readers corrected me.)
Unfortunately, that is not quite good enough because we do not actually have the logarithm in base 2, but rather a truncated version of it. Thus you may need to do a off-by-one correction. The following code works:
static uint32_t table[] = {9, 99, 999, 9999, 99999,
999999, 9999999, 99999999, 999999999};
int y = (9 * int_log2(x)) >> 5;
y += x > table[y];
return y + 1;
It might compile to the following assembly:
or eax, 1
bsr eax, eax
lea eax, [rax + 8*rax]
shr eax, 5
cmp dword ptr [4*rax + table], edi
adc eax, 0
add eax, 1
Loading from the table probably incurs multiple cycles of latency (e.g., 3 or 4). The x64 bsr
instruction has also a long latency of 3 or 4 cycles. My code is available.
You can port this function to Java as follows if you assume that the number is non-negative:
int l2 = 31 - Integer.numberOfLeadingZeros(num|1);
int ans = ((9*l2)>>>5);
if (num > table[ans]) { ans += 1; }
return ans + 1;
I wrote this blog post to answer a question by Chris Seaton on Twitter. After writing it up, I found that the always-brilliant Travis Downs had proposed a similar solution with a table lookup. I believe he requires a larger table. Robert Clausecker once posted a solution that might be close to what Travis has in mind.
Furthermore, if the number of digits is predictable, then you can write code with branches and get better results in some cases. However, you should be concerned with the fact that a single branch miss can cost you 15 cycles and tens of instructions.
Update: There is a followup to this blog post… Computing the number of digits of an integer even faster
Further reading: Converting binary integers to ASCII character and Integer log 10 of an unsigned integer — SIMD version
Note: Victor Zverovich stated on Twitter than the fmt C++ library relies on a similar approach. Pete Cawley showed that you could achieve the same result that I got initially by multiplying by 77 and then shifting by 8, instead of my initially larger constants. He implemented his solution for LuaJIt. Giulietti pointed out to me by email that almost exactly the same routine appears in Hacker’s Delight at the end of chapter 11.