I have a program in which it is necessary to calculate the floor of the log-base-2 of an integer very frequently. As a reuslt, the performance of the standard library's log2 method in my language of choice (floor(std::log2([INT])) from <cmath> in C++) is unsatisfactory, and I would like to implement the quickest version of this algorithm possible. I have found versions online which use bitwise operators to calculate this value for 32-bit and 64-bit integers:
INT Y(log2i)(const INT m)
{
/* Special case, zero or negative input. */
if (m <= 0)
return -1;
#if SIZEOF_PTRDIFF_T == 8
/* Hash map with return values based on De Bruijn sequence. */
static INT debruijn[64] =
{
0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3, 61, 51, 37, 40, 49,
18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4, 62, 57, 46, 52, 38, 26, 32, 41,
50, 36, 17, 19, 29, 10, 13, 21, 56, 45, 25, 31, 35, 16, 9, 12, 44, 24, 15,
8, 23, 7, 6, 5, 63
};
register uint64_t v = (uint64_t)(m);
/* Round down to one less than a power of 2. */
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
/* 0x03f6eaf2cd271461 is a hexadecimal representation of a De Bruijn
* sequence for binary words of length 6. The binary representation
* starts with 000000111111. This is required to make it work with one less
* than a power of 2 instead of an actual power of 2.
*/
return debruijn[(uint64_t)(v * 0x03f6eaf2cd271461LU) >> 58];
#elif SIZEOF_PTRDIFF_T == 4
/* Hash map with return values based on De Bruijn sequence. */
static INT debruijn[32] =
{
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28,
15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
register uint32_t v = (uint32_t)(m);
/* Round down to one less than a power of 2. */
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
/* 0x07C4ACDD is a hexadecimal representation of a De Bruijn sequence for
* binary words of length 5. The binary representation starts with
* 0000011111. This is required to make it work with one less than a power of
* 2 instead of an actual power of 2.
*/
return debruijn[(uint32_t)(v * 0x07C4ACDDU) >> 27];
#else
#error Incompatible size of ptrdiff_t
#endif
}
(Above code taken from this link; the comments of said code reference this page, which gives a brief overview of how the algorithm works).
I need to implement a version of this algorithm for 256-bit integers. The general form, for an n-bit integer, is fairly easy to understand: (1) create an array of integers from the Debruijn sequence with n entries; (2) perform in-place bitwise-or on the integer in question right-shifted by 2^i for i=1...(n/2); and (3) return some the entry of the Debruijn array with an index equal to the integer multiplied by a constant right-shifted by another constant.
The third step is where I'm confused. How exactly does one derive 0x07C4ACDDU and 0x03f6eaf2cd271461LU as the multiplication constants for 32 and 64 bits, respectively? How does one derive 58 and 27 as the constants by which one should right-shift? What would these values be for a 256-bit integer in particular?
Thanks in advance. Sorry if this is obvious, I'm not very educated in math.