【发布时间】:2010-09-08 08:45:29
【问题描述】:
我所追求的是我可以输入一个数字的东西,它会返回最高位。我相信有一个简单的方法。下面是一个示例输出(左边是输入)
1 -> 1 2 -> 2 3 -> 2 4 -> 4 5 -> 4 6 -> 4 7 -> 4 8 -> 8 9 -> 8 ... 63 -> 32
【问题讨论】:
标签: c
我所追求的是我可以输入一个数字的东西,它会返回最高位。我相信有一个简单的方法。下面是一个示例输出(左边是输入)
1 -> 1 2 -> 2 3 -> 2 4 -> 4 5 -> 4 6 -> 4 7 -> 4 8 -> 8 9 -> 8 ... 63 -> 32
【问题讨论】:
标签: c
我非常喜欢的最佳算法是:
unsigned hibit(unsigned n) {
n |= (n >> 1u);
n |= (n >> 2u);
n |= (n >> 4u);
n |= (n >> 8u);
n |= (n >> 16u);
return n - (n >> 1);
}
而且它很容易像这样扩展为 uint64_t:
uint64_t hibit(uint64_t n) {
n |= (n >> 1u);
n |= (n >> 2u);
n |= (n >> 4u);
n |= (n >> 8u);
n |= (n >> 16u);
n |= (n >> 32u);
return n - (n >> 1);
}
甚至到 __int128
__int128 hibit(__int128 n) {
n |= (n >> 1u);
n |= (n >> 2u);
n |= (n >> 4u);
n |= (n >> 8u);
n |= (n >> 16u);
n |= (n >> 32u);
n |= (n >> 64u);
return n - (n >> 1);
}
另外是跨平台解决方案独立于使用编译器
【讨论】:
这次聚会有点晚了,但我找到的最简单的解决方案是:将现代 GCC 作为编译器:
static inline int_t get_msb32 (register unsigned int val)
{
return 32 - __builtin_clz(val);
}
static inline int get_msb64 (register unsigned long long val)
{
return 64 - __builtin_clzll(val);
}
它甚至是相对可移植的(至少它可以在任何 GCC 平台上运行)。
【讨论】:
Linux 内核有许多像这样方便的位图,以最有效的方式编码,适用于多种架构。您可以在include/asm-generic/bitops/fls.h(和朋友)中找到通用版本,但如果速度至关重要,而可移植性不是,请参阅include/asm-x86/bitops.h 了解使用内联汇编的定义。
【讨论】:
如果您不需要可移植的解决方案并且您的代码在 x86 兼容的 CPU 上执行,您可以使用 Microsoft Visual C/C++ 编译器提供的 _BitScanReverse() 内部函数。它映射到返回最高位集合的 BSR CPU 指令。
【讨论】:
我想出的一个很好的解决方案是对位进行二进制搜索。
uint64_t highestBit(uint64_t a, uint64_t bit_min, uint64_t bit_max, uint16_t bit_shift){
if(a == 0) return 0;
if(bit_min >= bit_max){
if((a & bit_min) != 0)
return bit_min;
return 0;
}
uint64_t bit_mid = bit_max >> bit_shift;
bit_shift >>= 1;
if((a >= bit_mid) && (a < (bit_mid << 1)))
return bit_mid;
else if(a > bit_mid)
return highestBit(a, bit_mid, bit_max, bit_shift);
else
return highestBit(a, bit_min, bit_mid, bit_shift);
}
最大位是 2 的最高幂,因此对于 64 位数字,它将是 2^63。位移应该初始化为位数的一半,因此对于 64 位,它将是 32。
【讨论】:
这可以通过现有的库调用轻松解决。
int highestBit(int v){
return fls(v) << 1;
}
Linux 手册页提供了有关此函数及其对应输入类型的更多详细信息。
【讨论】:
(CHAR_BIT*sizeof(long long) - __builtin_clzll(v),但我不认为这个公式足够便携,不需要它。 <limits.h>
1UL << fls(v)。
fls 在许多体系结构上都达到了硬件指令的最低点。我怀疑这可能是最简单、最快的方法。
1<<(fls(input)-1)
【讨论】:
fls(0) -> 0,留下 << 的 rhs 为负数,未定义。在我的辩护中,这在问题空间中没有说明;-)
// Note doesn't cover the case of 0 (0 returns 1)
inline unsigned int hibit( unsigned int x )
{
unsigned int log2Val = 0 ;
while( x>>=1 ) log2Val++; // eg x=63 (111111), log2Val=5
return 1 << log2Val ; // finds 2^5=32
}
【讨论】:
一种快速的方法是通过查找表。对于 32 位输入和 8 位查找表,in 只需要 4 次迭代:
int highest_order_bit(int x)
{
static const int msb_lut[256] =
{
0, 0, 1, 1, 2, 2, 2, 2, // 0000_0000 - 0000_0111
3, 3, 3, 3, 3, 3, 3, 3, // 0000_1000 - 0000_1111
4, 4, 4, 4, 4, 4, 4, 4, // 0001_0000 - 0001_0111
4, 4, 4, 4, 4, 4, 4, 4, // 0001_1000 - 0001_1111
5, 5, 5, 5, 5, 5, 5, 5, // 0010_0000 - 0010_0111
5, 5, 5, 5, 5, 5, 5, 5, // 0010_1000 - 0010_1111
5, 5, 5, 5, 5, 5, 5, 5, // 0011_0000 - 0011_0111
5, 5, 5, 5, 5, 5, 5, 5, // 0011_1000 - 0011_1111
6, 6, 6, 6, 6, 6, 6, 6, // 0100_0000 - 0100_0111
6, 6, 6, 6, 6, 6, 6, 6, // 0100_1000 - 0100_1111
6, 6, 6, 6, 6, 6, 6, 6, // 0101_0000 - 0101_0111
6, 6, 6, 6, 6, 6, 6, 6, // 0101_1000 - 0101_1111
6, 6, 6, 6, 6, 6, 6, 6, // 0110_0000 - 0110_0111
6, 6, 6, 6, 6, 6, 6, 6, // 0110_1000 - 0110_1111
6, 6, 6, 6, 6, 6, 6, 6, // 0111_0000 - 0111_0111
6, 6, 6, 6, 6, 6, 6, 6, // 0111_1000 - 0111_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1000_0000 - 1000_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1000_1000 - 1000_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1001_0000 - 1001_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1001_1000 - 1001_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1010_0000 - 1010_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1010_1000 - 1010_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1011_0000 - 1011_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1011_1000 - 1011_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1100_0000 - 1100_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1100_1000 - 1100_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1101_0000 - 1101_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1101_1000 - 1101_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1110_0000 - 1110_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1110_1000 - 1110_1111
7, 7, 7, 7, 7, 7, 7, 7, // 1111_0000 - 1111_0111
7, 7, 7, 7, 7, 7, 7, 7, // 1111_1000 - 1111_1111
};
int byte;
int byte_cnt;
for (byte_cnt = 3; byte_cnt >= 0; byte_cnt--)
{
byte = (x >> (byte_cnt * 8)) & 0xff;
if (byte != 0)
{
return msb_lut[byte] + (byte_cnt * 8);
}
}
return -1;
}
【讨论】:
这应该可以解决问题。
int hob (int num)
{
if (!num)
return 0;
int ret = 1;
while (num >>= 1)
ret <<= 1;
return ret;
}
hob(1234) 返回 1024
hob(1024) 返回 1024
hob(1023) 返回 512
【讨论】:
喜欢混淆代码?试试这个:
1
【讨论】:
std::log2() 为负参数返回nan,int(nan) 的计算结果为0x80000000,这是最大的负整数。这可以在 g++7.3 中观察到,但恐怕这不是可移植的。下一个问题是舍入误差。当您输入 64 位数字时,在 2^{48}-1 内一切正常。但是对于 2^{49}-1,配方返回 2^{49}。
来自黑客的喜悦:
int hibit(unsigned int n) {
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return n - (n >> 1);
}
此版本适用于 32 位整数,但逻辑可以扩展为 64 位或更高。
【讨论】:
不断删除想到的低位......
int highest_order_bit( int x )
{
int y = x;
do {
x = y;
y = x & (x-1); //remove low order bit
}
while( y != 0 );
return x;
}
【讨论】: