Quick integer logarithm for special case

Why not use the next two bits other than the leading bit. You can first partition the number into the 8 bin, and the next two bits to further divide each bin into four. In this case, you can use a simple shift operation which is very fast.

Edit: If you think using the logarithm is a viable solution. Here is the general algorithm:

Let a be the base of the logarithm, and the range is (b_min, b_max) = (32,8191). You can find the base using the formula:

log(b_max/b_min) / log(a) = 32 bin

which give you a~1.1892026. If you use this a as the base of the logarithm, you can map the range (b_min, b_max) into (log_a(b_min), log_a(b_max)) = (20.0004,52.0004).

Now you only need to subtract the all element by a 20.0004 to get the range (0,32). It guarantees all elements are logarithmically uniform. Done

Note: Either a element may fall out of range because of numerical error. You should calculate it yourself for the exact value.

Note2: log_a(b) = log(b)/log(a)


Table lookup is one option, that table isn't that big. If an 8K table is too big, and you have a count leading zeros instruction, you can use a table lookup on the top few bits.

nbits = 32 - count_leading_zeros(v)  # number of bits in number
highbits = v >> (nbits - 4)          # top 4 bits.  Top bit is always a 1.
log_base_2 = nbits + table[highbits & 0x7]

The table you populate with some approximation of log_2

table[i] = approx(log_2(1 + i/8.0))

If you want to stay in integer arithmetic, multiply the last line by a convenient factor.


Answer I just came up with based in IEEE 754 floating point:

((union { float v; uint32_t r; }){ x }.r >> 21 & 127) - 16

It maps 32-8192 onto 0-31 roughly logarithmically (same as hwlau's answer).

Improved version (cut out useless bitwise and):

((union { float v; uint32_t r; }){ x }.r >> 21) - 528