找到2的幂的最快方法是什么,使用一定数量(即2的幂)?
我对数学不是很熟练,所以我不确定如何最好地描述它。但该函数看起来与x = 2^y
类似,其中y
是输出,x
是输入。这是一个真实表,如果有助于解释它的外观。
0 = f(1)
1 = f(2)
2 = f(4)
3 = f(8)
...
8 = f(256)
9 = f(512)
我已经创造了一个能够做到这一点的功能,但我担心它不是很有效(或者说优雅)。这样做会有更简单,更有效的方法吗?我正在使用它来计算纹理的哪个区域用于缓冲绘制的完成方式,因此每个绘制的对象至少调用一次。这是我到目前为止所做的功能:
uint32 getThePowerOfTwo(uint32 value){
for(uint32 n = 0; n < 32; ++n){
if(value <= (1 << n)){
return n;
}
}
return 32; // should never be called
}
答案 0 :(得分:8)
以woolstar的答案为基础 - 我想知道查找表的二进制搜索是否稍快一点? (看起来好多了)......
int getThePowerOfTwo(int value) {
static constexpr int twos[] = {
1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30, 1<<31
};
return std::lower_bound(std::begin(twos), std::end(twos), value) - std::begin(twos);
}
答案 1 :(得分:4)
此操作非常适合处理器供应商提供硬件支持。查看find first set。编译器供应商为此提供了特定的功能,遗憾的是似乎没有标准如何命名它。因此,如果您需要最高性能,则必须创建依赖于编译器的代码:
# ifdef __GNUC__
return __builtin_ffs( x ) - 1; // GCC
#endif
#ifdef _MSC_VER
return CHAR_BIT * sizeof(x)-__lzcnt( x ); // Visual studio
#endif
答案 2 :(得分:1)
如果输入值仅为2^n
n
- 整数,则查找n
的最佳方法是将哈希表与perfect hash function一起使用。在这种情况下,32个无符号整数的散列函数可以定义为value % 37
template < size_t _Div >
std::array < uint8_t, _Div > build_hash()
{
std::array < uint8_t, _Div > hash_;
std::fill(hash_.begin(), hash_.end(), std::numeric_limits<uint8_t>::max());
for (size_t index_ = 0; index_ < 32; ++index_)
hash_[(1 << index_) % _Div] = index_;
return hash_;
}
uint8_t hash_log2(uint32_t value_)
{
static const std::array < uint8_t, 37 > hash_ = build_hash<37> ();
return hash_[value_%37];
}
检查
int main()
{
for (size_t index_ = 0; index_ < 32; ++index_)
assert(hash_log2(1 << index_) == index_);
}
答案 3 :(得分:0)
您的版本很好,但正如您所推测的那样,它的O(n)
意味着每一位都需要一步完成循环。你可以做得更好。为了实现下一步,尝试相当于分而治之:
unsigned int log2(unsigned int value)
{
unsigned int val = 0 ;
unsigned int mask= 0xffff0000 ;
unsigned int step= 16 ;
while ( value )
{
if ( value & mask ) { val += step ; value &= ~ mask ; }
step /= 2 ;
if ( step ) { mask >>= step ; } else { mask >>= 1 ; }
}
return val ;
}
由于我们只是寻找最高位,我们开始询问该字的上半部分是否有任何位。如果有,我们可以丢弃所有较低位,否则我们只是缩小搜索范围。
由于问题已标记为C++
,因此这是一个使用模板的版本,试图找出初始掩码&amp;步骤:
template <typename T>
T log2(T val)
{
T result = 0 ;
T step= ( 4 * sizeof( T ) ) ; // half the number of bits
T mask= ~ 0L - ( ( 1L << ( 4 * sizeof( T )) ) -1 ) ;
while ( val && step )
{
if ( val & mask ) { result += step ; val >>= step ; }
mask >>= ( step + 1) / 2 ;
step /= 2 ;
}
return result ;
}
虽然任何一个版本的性能都将成为现代x86架构的一个亮点,但在嵌入式解决方案中我已经找到了这一点,在最后一个案例中我解决了与此类似的搜索,甚至是{ {1}}对于中断来说太慢了,我们不得不使用除法和征服以及表查找的组合来挤出最后几个周期。
答案 4 :(得分:0)
如果您知道它确实是2的幂(很容易验证), 请尝试以下变体。 完整说明如下:http://sree.kotay.com/2007/04/shift-registers-and-de-bruijn-sequences_10.html
//table
static const int8 xs_KotayBits[32] = {
0, 1, 2, 16, 3, 6, 17, 21,
14, 4, 7, 9, 18, 11, 22, 26,
31, 15, 5, 20, 13, 8, 10, 25,
30, 19, 12, 24, 29, 23, 28, 27
};
//only works for powers of 2 inputs
static inline int32 xs_ILogPow2 (int32 v){
assert (v && (v&(v-1)==0));
//constant is binary 10 01010 11010 00110 01110 11111
return xs_KotayBits[(uint32(v)*uint32( 0x04ad19df ))>>27];
}