Count number of digits - which method is most efficient?

The following is even more efficient:

int findn(int num)
{
   if ( num < 10 )
      return 1;
   if ( num < 100 )
      return 2;
   //continue until max int
}

You could optimize this even further by doing a binary search, but that would be overkill.


As it currently stands, the accepted and most highly approved answer is (still) incorrect for negative numbers. If the answerer were to take the time to test it and find out that it's broken for negative numbers, he likely would have wasted more time than the machine ever would by simply using snprintf, i.e.

int count_digits(int arg) {
    return snprintf(NULL, 0, "%d", arg) - (arg < 0);
}

We're not in the 1980s anymore; stop coding as though we are. I'm a C-standard zealot and my favourite answer given here was Tao Feng's answer... but even that didn't go into why it's the most efficient answer so far; in this answer I intend to show that his answer can be improved further by considering the following:

  • Programmer productivity is more important than code efficiency, because it will almost certainly cost more time to write and test new functions properly than it will for a few microseconds of runtime.
  • Reusing the same standard library functions that other programs commonly use (probably) keeps those standard libraries in the CPU cache. A cache miss (for example, when your code needs to be copied from RAM into the CPU) can cost up to 50 CPU instructions, not to mention the other code may end up causing another cache miss to put snprintf back into the cache anyway.
  • Eliminating storage requirements might expose extra optimisations.

The following describes the micro-optimisation which hinders your productivity. Due to the lack of information you've provided in your answer, nobody who answers the question as it currently stands can provide any proof without making assumptions about:

  • When we optimise we need to find the most significant bottleneck in the full solution (the problem that your program's designed to solve). There are two possibilities here: A) You want to calculate the number of bytes to allocate in order to store a string containing these digits; B) You just want to count the number of digits or whatever for kicks. More on these later. For now it's important to realise you're probably talking about part of a solution, and that part might not be the most significant bottleneck.
  • The compiler you're using, the OS you're using and the machine you're using (including RAM speed, since some of us are introducing potential cache misses that are affected more by slow memory than by fast memory) might affect the most significant bottleneck. Some compilers are different to others, and will optimise some pieces of code better for some OSes, CPUs, etc than others.

You can avoid micro-optimisation by measuring bottlenecks, i.e. by profiling ("benchmarking") each of these solutions on your system, assuming they even solve your problems properly. If a solution doesn't solve the problem, it's not a solution, so it shouldn't be considered... When done correctly this should eliminate the micro-optimisation. Some compilers even provide intelligent profile-guided optimisation which commonly shaves 20-30% by reorganising branches and objects for cache locality, and do so automatically.

I've already covered counting digits, which I think most certainly answers your question, but there are cases where you might think you need to count digits when you don't, and the ability to remove the overhead of counting digits might present a very desirable optimisation, both in man hours and in machine hours.

For example, if you want to calculate the number of bytes to allocate in order to store a string containing these digits, you shouldn't use any runtime because a preprocessor macro can be used to calculate the maximum number of digits (or characters, including the sign), and any precious bytes of temporary storage you try to save will be well outnumbered by machine code bytes added in logic, which seems like a steep cost to me. There's also a benefit for the programmer to use a preprocessor macro; the same macro could be used for any integer type. See my answer to this question for a solution to this problem; after all, there's no point repeating myself...


The GCC/Clang __builtin_clz() or Microsoft Visual C _BitScanReverse() intrinsic functions compile to a single machine instruction on many machines. You can use this as the basis for an O(1) solution. Here's a 32-bit implementation:

#include <limits.h>
#include <stdint.h>

/* Return the number of digits in the decimal representation of n. */
unsigned digits(uint32_t n) {
    static uint32_t powers[10] = {
        0, 10, 100, 1000, 10000, 100000, 1000000,
        10000000, 100000000, 1000000000,
    };
    static unsigned maxdigits[33] = {
        1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5,
        5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 
    };
    unsigned bits = sizeof(n) * CHAR_BIT - __builtin_clz(n);
    unsigned digits = maxdigits[bits];
    if (n < powers[digits - 1]) {
        -- digits;
    }
    return digits;
}