如是否落在2 ^ 3 - 2 ^ 4,2 ^ 4 - 2 ^ 5等内。返回的数字将是EXPONENT本身(减去偏移量)。
如何尽可能快速有效地完成这项工作?这个函数将在一个非常依赖于速度的程序中被调用。这是我当前的代码,但它使用for循环效率太低了。
static inline size_t getIndex(size_t numOfBytes)
{
int i = 3;
for (; i < 32; i++)
{
if (numOfBytes < (1 << i))
return i - OFFSET;
}
return (NUM_OF_BUCKETS - 1);
}
非常感谢!
答案 0 :(得分:9)
据我所知,你所追求的只是log 2 ( n )。
如果您的目标体系结构具有可以执行此操作的指令,则可能值得作弊并使用一些内联汇编。有关硬件支持的大量讨论和信息,请参阅“{first}”上的Wikipedia entry。
答案 1 :(得分:5)
这样做的一种方法是找到设置为1的最高位。我试着想一想这是否有效,因为在最坏的情况下你仍然需要进行n次检查。
也许你可以做二进制搜索样式,你检查它是否大于2 ^ 16,如果是,检查它是否大于2 ^ 24(假设这里是32位),如果没有,那么检查它是否大于2 ^ 20等...这将是log(n)检查,但我不确定比特检查与完整int比较的效率。
可以在其中获得一些性能数据。
答案 2 :(得分:1)
使用Sean Eron Anderson的优秀Bit Twiddling Hacks页面描述的de Bruijn序列有一种特别有效的算法:
uint32_t v; // find the log base 2 of 32-bit v
int r; // result goes here
static const int MultiplyDeBruijnBitPosition[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
};
v |= v >> 1; // first round down to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];
它可以在没有分支的13个操作中运行!
答案 3 :(得分:1)
您基本上是在尝试计算:floor(log2(x))
以对数为基数2,然后发言。
在C中执行此操作的最便携方式是使用logf()
函数,该函数查找基本 e 的日志,然后调整:log2(x) == logf(x) / logf(2.0)
请在此处查看答案:How to write log base(2) in c/c++
如果您只是将生成的浮点值转换为int
,则可以同时计算floor()
。
但是,如果您可以使用它并且可以使用它,那么计算浮点数log2()
的速度非常快:logbf()
从手册页:
The inte-
ger constant FLT_RADIX, defined in <float.h>, indicates the radix used
for the system's floating-point representation. If FLT_RADIX is 2,
logb(x) is equal to floor(log2(x)), except that it is probably faster.
http://linux.die.net/man/3/logb
如果您考虑如何存储浮点数,您会发现值floor(log2(x))
是数字的一部分,如果您只是提取该值,那么您就完成了。一点点移位和位掩码,并从指数中减去偏差(或者从技术上减去“有效数”),你就可以得到它。为任何浮点值floor(log2(x))
计算x
的最快方法。
http://en.wikipedia.org/wiki/Single_precision
但实际上logbf()
会在将结果提供给您之前将结果转换为浮点数,并处理错误。如果你编写自己的函数来将指数提取为整数,那么它会稍快一些,无论如何都是你想要的整数。如果你想编写自己的函数,你需要使用C union
来访问浮点内的位;尝试使用指针将获得与“打字”相关的警告或错误,至少在GCC上。如果你问,我会详细介绍如何做到这一点。我之前编写过这段代码,作为inline
函数。
如果您只需测试一小部分数字,则可以将数字转换为整数,然后使用查找表。
答案 4 :(得分:1)
您可以使用浮动数字表示:
double n_bytes = numOfBytes
取指数位应该给出结果,因为浮点数表示为:
(-1)^S X (1. + M) X 2^E
其中: S - 签名 M - Mantissa E - 指数
要构造掩码和移位,您必须阅读有关正在使用的浮点类型的确切位模式。
CPU浮点支持为您完成大部分工作。
更好的方法是使用内置函数:
double frexp (double x, int * exp );
答案 5 :(得分:0)
在具有硬件浮点单元的任何机器上通常都很快:
((union { float val; uint32_t repr; }){ x }.repr >> 23) - 0x7f
它做出的唯一假设是浮点是IEEE和整数和浮点字节序匹配,这两者在基本上所有真实世界系统(当然都是现代系统)中都是正确的。
修改:过去我使用过此功能时,我不需要大数字。 Eric指出,对于不适合浮动的整数,它会给出错误的结果。这是一个修订版(虽然可能更慢),修复了该版本并支持最多52位的值(特别是所有32位正整数输入):
((union { double val; uint64_t repr; }){ x }.repr >> 52) - 0x3ff
另请注意,我假设x
是正数(不仅是非负数,也是非零数)。如果x
为负数,则会得到虚假结果,如果x
为0,则会得到较大的负结果(近似负对数为对数)。
答案 6 :(得分:0)
#include <Limits.h> // For CHAR_BIT.
#include <math.h> // For frexp.
#include <stdio.h> // For printing results, as a demonstration.
// These routines assume 0 < x.
/* This requires GCC (or any other compiler that supplies __builtin_clz). It
should perform well on any machine with a count-leading-zeroes instruction
or something similar.
*/
static int log2A(unsigned int x)
{
return sizeof x * CHAR_BIT - 1 - __builtin_clz(x);
}
/* This requires that a double be able to exactly represent any unsigned int.
(This is true for 32-bit integers and 64-bit IEEE 754 floating-point.) It
might perform well on some machines and poorly on others.
*/
static int log2B(unsigned int x)
{
int exponent;
frexp(x, &exponent);
return exponent - 1;
}
int main(void)
{
// Demonstrate the routines.
for (unsigned int x = 1; x; x <<= 1)
printf("0x%08x: log2A -> %2d, log2B -> %2d.\n", x, log2A(x), log2B(x));
return 0;
}