Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Quick integer logarithm for special case

I have integer values ranging from 32-8191 which I want to map to a roughly logarithmic scale. If I were using base 2, I could just count the leading zero bits and map them into 8 slots, but this is too course-grained; I need 32 slots (and more would be better, but I need them to map to bits in a 32-bit value), which comes out to a base of roughly 1.18-1.20 for the logarithm. Anyone have some tricks for computing this value, or a reasonable approximation, very fast?

My intuition is to break the range down into 2 or 3 subranges with conditionals, and use a small lookup table for each, but I wonder if there's some trick I could do with count-leading-zeros then refining the result, especially since the results don't have to be exact but just roughly logarithmic.

like image 522
R.. GitHub STOP HELPING ICE Avatar asked Dec 11 '10 04:12

R.. GitHub STOP HELPING ICE


2 Answers

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)

like image 60
unsym Avatar answered Sep 21 '22 00:09

unsym


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.

like image 38
Keith Randall Avatar answered Sep 22 '22 00:09

Keith Randall