Fixed cost version using x86 build and lookup table:
int count_bsr(int i) { struct { int max; int count; } static digits[32] = { { 9, 1 }, { 9, 1 }, { 9, 1 }, { 9, 1 }, { 99, 2 }, { 99, 2 }, { 99, 2 }, { 999, 3 }, { 999, 3 }, { 999, 3 }, { 9999, 4 }, { 9999, 4 }, { 9999, 4 }, { 9999, 4 }, { 99999, 5 }, { 99999, 5 }, { 99999, 5 }, { 999999, 6 }, { 999999, 6 }, { 999999, 6 }, { 9999999, 7 }, { 9999999, 7 }, { 9999999, 7 }, { 9999999, 7 }, { 99999999, 8 }, { 99999999, 8 }, { 99999999, 8 }, { 999999999, 9 }, { 999999999, 9 }, { 999999999, 9 }, { INT_MAX, 10 }, { INT_MAX, 10 } }; register const int z = 0; register unsigned log2; if (i < 0) i = -i; __asm__ __volatile__ ( "bsr %1, %0;" \ "cmovz %2, %0;"\ : "=r" (log2) \ : "rm" (i), "r"(z)); return digits[log2].count + ( i > digits[log2].max ); }
Another one with a smaller lookup table and a log10 approximation taken from here .
int count_bsr2( int i ) { static const unsigned limits[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; register const int z = 0; register int l, log2; if (i < 0) i = -i; __asm__ __volatile__ ( "bsr %1, %0;" \ "cmovz %2, %0;"\ : "=r" (log2) \ : "rm" (i), "r"(z)); l = (log2 + 1) * 1233 >> 12; return (l + ((unsigned)i >= limits[l])); }
Both of them use the fact that on x86 -INT_MIN is INT_MIN.
Update:
As suggested, here are the timings for count_bsr and some faster 64-bit count_bsr_mod routines compared to binary search and binary algorithms using the very nice paxdiablo testing program, modified to generate sets with random sign distribution. Tests were built using gcc 4.9.2 using the options "-O3 -falign-functions = 16 -falign-jumps = 16 -march = corei7-avx" and were run in a quiet Sandy Bridge system with turbo and sleep modes turned off.
Time for bsr mod: 270000
Time for bsr: 340000
Time for binary chop: 800000
Time for binary search: 770000
Time for binary search mod: 470000
Source for the test,
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <limits.h> #include <time.h> #define numof(a) (sizeof(a) / sizeof(a[0])) /* Random numbers and accuracy checks. */ static int rndnum[10000]; static int rt[numof(rndnum)]; /* All digit counting functions here. */ static int count_bchop (int n) { int r = 1; if (n < 0) n = (n == INT_MIN) ? INT_MAX : -n; if (n >= 100000000) { r += 8; n /= 100000000; } if (n >= 10000) { r += 4; n /= 10000; } if (n >= 100) { r += 2; n /= 100; } if (n >= 10) r++; return r; } static int count_bsearch(int i) { if (i < 0) { if (i == INT_MIN) return 11; // special case for -2^31 because 2^31 can't fit in a two complement 32-bit integer i = -i; } if (i < 100000) { if (i < 1000) { if (i < 10) return 1; else if (i < 100) return 2; else return 3; } else { if (i < 10000) return 4; else return 5; } } else { if (i < 10000000) { if (i < 1000000) return 6; else return 7; } else { if (i < 100000000) return 8; else if (i < 1000000000) return 9; else return 10; } } } // Integer log base 10, modified binary search. static int count_bsearch_mod(int i) { unsigned x = (i >= 0) ? i : -i; if (x > 99) if (x > 999999) if (x > 99999999) return 9 + (x > 999999999); else return 7 + (x > 9999999); else if (x > 9999) return 5 + (x > 99999); else return 3 + (x > 999); else return 1 + (x > 9); } static int count_bsr_mod(int i) { struct { int m_count; int m_threshold; } static digits[32] = { { 1, 9 }, { 1, 9 }, { 1, 9 }, { 1, 9 }, { 2, 99 }, { 2, 99 }, { 2, 99 }, { 3, 999 }, { 3, 999 }, { 3, 999 }, { 4, 9999 }, { 4, 9999 }, { 4, 9999 }, { 4, 9999 }, { 5, 99999 }, { 5, 99999 }, { 5, 99999 }, { 6, 999999 }, { 6, 999999 }, { 6, 999999 }, { 7, 9999999 }, { 7, 9999999 }, { 7, 9999999 }, { 7, 9999999 }, { 8, 99999999 }, { 8, 99999999 }, { 8, 99999999 }, { 9, 999999999 }, { 9, 999999999 }, { 9, 999999999 }, { 10, INT_MAX }, { 10, INT_MAX } }; __asm__ __volatile__ ( "cdq \n\t" "xorl %%edx, %0 \n\t" "subl %%edx, %0 \n\t" "movl %0, %%edx \n\t" "bsrl %0, %0 \n\t" "shlq $32, %%rdx \n\t" "movq %P1(,%q0,8), %q0 \n\t" "cmpq %q0, %%rdx \n\t" "setg %%dl \n\t" "addl %%edx, %0 \n\t" : "+a"(i) : "i"(digits) : "rdx", "cc" ); return i; } static int count_bsr(int i) { struct { int max; int count; } static digits[32] = { { 9, 1 }, { 9, 1 }, { 9, 1 }, { 9, 1 }, { 99, 2 }, { 99, 2 }, { 99, 2 }, { 999, 3 }, { 999, 3 }, { 999, 3 }, { 9999, 4 }, { 9999, 4 }, { 9999, 4 }, { 9999, 4 }, { 99999, 5 }, { 99999, 5 }, { 99999, 5 }, { 999999, 6 }, { 999999, 6 }, { 999999, 6 }, { 9999999, 7 }, { 9999999, 7 }, { 9999999, 7 }, { 9999999, 7 }, { 99999999, 8 }, { 99999999, 8 }, { 99999999, 8 }, { 999999999, 9 }, { 999999999, 9 }, { 999999999, 9 }, { INT_MAX, 10 }, { INT_MAX, 10 } }; register const int z = 0; register unsigned log2; if (i < 0) i = -i; __asm__ __volatile__ ( "bsr %1, %0;" \ "cmovz %2, %0;"\ : "=r" (log2) \ : "rm" (i), "r"(z)); return digits[log2].count + ( i > digits[log2].max ); } /* Structure to control calling of functions. */ typedef struct { int (*fnptr)(int); const char *desc; } tFn; static tFn fn[] = { { NULL, NULL }, { count_bsr_mod, " bsr mod" }, { count_bsr, " bsr" }, { count_bchop, " binary chop" }, { count_bsearch, " binary search" }, { count_bsearch_mod," binary search mod"} }; static clock_t clk[numof (fn)]; int main (int c, char *v[]) { int i, j, k, r; int s = 1; /* Test code: printf ("%11d %d\n", INT_MIN, count_bsearch(INT_MIN)); //for (i = -1000000000; i != 0; i /= 10) for (i = -999999999; i != 0; i /= 10) printf ("%11d %d\n", i, count_bsearch(i)); printf ("%11d %d\n", 0, count_bsearch(0)); for (i = 1; i != 1000000000; i *= 10) printf ("%11d %d\n", i, count_bsearch(i)); printf ("%11d %d\n", 1000000000, count_bsearch(1000000000)); printf ("%11d %d\n", INT_MAX, count_bsearch(INT_MAX)); return 0; /* */ /* Randomize and create random pool of numbers. */ int p, n; p = n = 0; srand (time (NULL)); for (j = 0; j < numof (rndnum); j++) { rndnum[j] = ((rand() & 2) - 1) * rand(); } rndnum[0] = INT_MAX; rndnum[1] = INT_MIN; /* For testing. */ for (k = 0; k < numof (rndnum); k++) { rt[k] = (fn[1].fnptr)(rndnum[k]); } /* Test each of the functions in turn. */ clk[0] = clock(); for (i = 1; i < numof (fn); i++) { for (j = 0; j < 10000; j++) { for (k = 0; k < numof (rndnum); k++) { r = (fn[i].fnptr)(rndnum[k]); /* Test code: if (r != rt[k]) { printf ("Mismatch error [%s] %d %d %d %d\n", fn[i].desc, k, rndnum[k], rt[k], r); return 1; } /* */ } } clk[i] = clock(); } /* Print out results. */ for (i = 1; i < numof (fn); i++) { printf ("Time for %s: %10d\n", fn[i].desc, (int)(clk[i] - clk[i-1])); } return 0; }