====== Fast log2 of an unsigned int ====== //Proposed by Bouyack on 04/25/2008// This one is deceptively simple sounding. Write an function that finds the floor of log2 for a 32-bit unsigned integer. Make it as fast as possible. One thing: there is a solution to this problem that uses 4 Gb of RAM and may be the fastest in certain situations. That solution is retarded and doesn't count. Don't use more than a reasonable amount of memory, say, 10 Mb (though even that seems excessive for just finding log2(x)). Also, don't use assembly. Yes it will make your program run faster but then I would have to rewrite mine in assembly and I really don't want to bother with that. Anyways, it's the algorithm that's important. Good luck! ====== Discussion ====== ==== Cheating ==== Here's an implementation that cheats. It's very fast and always correct... as long as you start with log(1) then log(2), log(3), all the way up to log(0xFFFFFFFF). No cheating! unsigned int fake_log2(unsigned int x) { static unsigned int upper = 1; static unsigned int current = 1; static unsigned int next_ret = 0; unsigned int ret; ret = next_ret; current--; if(current == 0) { upper = upper << 1; current = upper; next_ret++; } return ret; } ==== Benchmark code ==== This code is listed on the Wikipedia page for the [[wp>Binary_logarithm|binary logarithm]] to solve this very problem. It generates an answer in O(1) time. Is there an algorithm that will solve it faster? //-Gerf// /** * Returns the floor form of binary logarithm for a 32 bit integer. * -1 is returned if n is 0. */ int floorLog2(unsigned int n) { int pos = 0; if (n >= 1<<16) { n >>= 16; pos += 16; } if (n >= 1<< 8) { n >>= 8; pos += 8; } if (n >= 1<< 4) { n >>= 4; pos += 4; } if (n >= 1<< 2) { n >>= 2; pos += 2; } if (n >= 1<< 1) { pos += 1; } return ((n == 0) ? (-1) : pos); } ==== Current Results ==== ''bouyack_log2: 9 seconds''\\ ''wikipedia_log2: 11 seconds''\\ ''fake_log2: 5 seconds''\\ Here's the code I used for testing: time_t t1, t2; volatile unsigned int unused; t1 = time(NULL); for(i=1;i!=0;i++) unused = bouyack_log2(i); t2 = time(NULL); printf("bouyack_log2: %d seconds\n",t2-t1); t1 = time(NULL); for(i=1;i!=0;i++) unused = wikipedia_log2(i); t2 = time(NULL); printf("wikipedia_log2: %d seconds\n",t2-t1); t1 = time(NULL); for(i=1;i!=0;i++) unused = fake_log2(i); t2 = time(NULL); printf("fake_log2: %d seconds\n",t2-t1); The ''volatile unsigned int unused'' keeps the compiler from optimizing away the function calls, which it would since they don't actually do anything. Before I added this all three of them took 2 seconds.