一个伟大的编程资源,位操作黑客,提出了( 这里 )下面的方法来计算一个32位整数的log 2:
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
static const char LogTable256[256] =
{
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};
unsigned int v; // 32-bit word to find the log of
unsigned r; // r will be lg(v)
register unsigned int t, tt; // temporaries
if (tt = v >> 16)
{
r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else
{
r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}
并提到
查找表方法只需约7操作,以找到一个32位的值的对数。 如果延长64位值,这将需要大约9的操作。
但是,唉,不给任何额外的信息关于哪种方式应该真正去算法扩展到64位整数。
有关如何这样的一个64位的算法看起来像任何提示吗?
Answer 1:
内在功能都非常快,但仍然不足以LOG2的真正的跨平台,编译器无关的实现。 所以,如果如果有人有兴趣,这里是最快的,无分支,CPU抽象DeBruijn样的算法我来,同时研究我自己的话题。
const int tab64[64] = {
63, 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};
int log2_64 (uint64_t value)
{
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value |= value >> 32;
return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
}
向下取整为2的下一个较低功率的一部分取自电源的-2边界和获得尾随零的数目的一部分取自BitScan (所述(bb & -bb)
码有挑出被设置为1最右边的位,我们已经四舍五入值下降到2的下一个动力,之后是不需要)。
而32位实现,顺便说一句,是
const int tab32[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};
int log2_32 (uint32_t value)
{
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return tab32[(uint32_t)(value*0x07C4ACDD) >> 27];
}
与任何其他计算方法,LOG2要求的输入值是大于零。
Answer 2:
如果你正在使用gcc,查找表是在这种情况下没有必要。
GCC提供了一个内置函数来确定前导零的数量:
内置功能: int __builtin_clz (unsigned int x)
返回的领导X 0位,从最高显著位位置的数量。 如果x为0,结果是不确定的。
所以,你可以定义:
#define LOG2(X) ((unsigned) (8*sizeof (unsigned long long) - __builtin_clzll((X)) - 1))
它适用于任何无符号长long int类型的工作。 结果向下取整。
对于x86和AMD64 GCC将其编译成bsr
指令,因此该解决方案是非常快(比查找表要快得多)。
工作示例 :
#include <stdio.h>
#define LOG2(X) ((unsigned) (8*sizeof (unsigned long long) - __builtin_clzll((X)) - 1))
int main(void) {
unsigned long long input;
while (scanf("%llu", &input) == 1) {
printf("log(%llu) = %u\n", input, LOG2(input));
}
return 0;
}
Answer 3:
我试图转换与乘法和查找查找为O N位整数的日志基地2(LG(N))业务为64位通过暴力破解的幻数。 不用说,它正在采取一段时间。
后来我发现Desmond的答案,并决定尝试他的魔术数字作为起点。 因为我有一个6核心处理器我跑它并联在起始0x07EDD5E59A4E28C2 / 6的倍数。 我很惊讶,立刻发现了什么。 原来0x07EDD5E59A4E28C2 / 2的工作。
因此,这里是为您节省了转变,减去用于0x07EDD5E59A4E28C2的代码:
int LogBase2(uint64_t n)
{
static const int table[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 };
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
return table[(n * 0x03f6eaf2cd271461) >> 58];
}
Answer 4:
基-2-整数对数
以下是我对64位无符号整数做。 此计算基本-2对数,这相当于最显著位的索引的楼层。 这种方法是快速smokingly为大量,因为它使用的展开循环,在log₂64= 6个步骤总是执行。
本质上,它的作用是在序列中减去远逐渐变小的平方{0≤ķ≤5:2 ^(2 ^ k)的} = {2³²,2 16,2⁸,2⁴,2 2,2 1} = {4294967296,65536,256 ,16,4,2,1}和求和指数减去值中的k。
int uint64_log2(uint64_t n)
{
#define S(k) if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; }
int i = -(n == 0); S(32); S(16); S(8); S(4); S(2); S(1); return i;
#undef S
}
请注意,这将返回-1,如果给定的0无效的输入(这是最初的-(n == 0)
被检查)。 如果你从来没有期望与调用它n == 0
,你可以代替int i = 0;
对于初始化,并添加assert(n != 0);
在进入该功能。
BASE-10的对数的整数
BASE-10整数对数可以用类似的计算 - 与最大的广场,以测试为10 16,因为log₁₀2⁶⁴≅19.2659 ...(注:这是不是为了实现一个基数为10整数对数最快的方法,因为它使用整数除法,这本质上是缓慢的。一个更快的实施将采用与指数增长值的蓄电池,并且比较对蓄压器,实际上做一种二进制搜索的。)
int uint64_log10(uint64_t n)
{
#define S(k, m) if (n >= UINT64_C(m)) { i += k; n /= UINT64_C(m); }
int i = -(n == 0);
S(16,10000000000000000); S(8,100000000); S(4,10000); S(2,100); S(1,10);
return i;
#undef S
}
Answer 5:
这里是一个非常紧凑和快速的延伸,不使用额外的临时:
r = 0;
/* If its wider than 32 bits, then we already know that log >= 32.
So store it in R. */
if (v >> 32)
{
r = 32;
v >>= 32;
}
/* Now do the exact same thing as the 32 bit algorithm,
except we ADD to R this time. */
if (tt = v >> 16)
{
r += (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else
{
r += (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}
这是一个与链构建if
S,再次使用无需额外的临时。 可能不是最快的,但。
if (tt = v >> 48)
{
r = (t = tt >> 8) ? 56 + LogTable256[t] : 48 + LogTable256[tt];
}
else if (tt = v >> 32)
{
r = (t = tt >> 8) ? 40 + LogTable256[t] : 32 + LogTable256[tt];
}
else if (tt = v >> 16)
{
r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else
{
r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}
Answer 6:
该算法基本上找出哪些字节包含最显著1位,然后查找该字节的查找,找到字节的日志,然后将其添加到字节的位置。
这里是32位算法的稍微简化的版本:
if (tt = v >> 16)
{
if (t = tt >> 8)
{
r = 24 + LogTable256[t];
}
else
{
r = 16 + LogTable256[tt];
}
}
else
{
if (t = v >> 8)
{
r = 8 + LogTable256[t];
}
else
{
r = LogTable256[v];
}
}
这相当于64位的算法:
if (ttt = v >> 32)
{
if (tt = ttt >> 16)
{
if (t = tt >> 8)
{
r = 56 + LogTable256[t];
}
else
{
r = 48 + LogTable256[tt];
}
}
else
{
if (t = ttt >> 8)
{
r = 40 + LogTable256[t];
}
else
{
r = 32 + LogTable256[ttt];
}
}
}
else
{
if (tt = v >> 16)
{
if (t = tt >> 8)
{
r = 24 + LogTable256[t];
}
else
{
r = 16 + LogTable256[tt];
}
}
else
{
if (t = v >> 8)
{
r = 8 + LogTable256[t];
}
else
{
r = LogTable256[v];
}
}
}
我想出了任何规模大小类型,我认为是比原来更好的算法。
unsigned int v = 42;
unsigned int r = 0;
unsigned int b;
for (b = sizeof(v) << 2; b; b = b >> 1)
{
if (v >> b)
{
v = v >> b;
r += b;
}
}
注意: b = sizeof(v) << 2
套b键v中的位的数目的一半我用移位来代替乘法这里(只是因为我觉得它)。
你可以查找表添加到该算法可能加速这一过程,但它更是一个验证的概念。
文章来源: Fast computing of log2 for 64-bit integers