得到一个数字的第二个最重要的位

时间:2015-08-24 18:15:41

标签: c binary bit-manipulation

你是一个给定整数i > 1的C程序员。你能编写一个函数msb2来返回它的第二个最重要的位,换句话说: 给出一个数字,最左边1位右边的位是什么? 我在1011111中它是0,在1100000中它是1。

不允许:

  • 任何循环(例如,分别检查每个64位)
  • 浮动算术

允许:

  • 所有C整数运算符

是否存在一个常数时间(位数的常数,假设所有C整数运算符占用恒定时间,并假设位数不是常数)msb2的函数?

我的一个想法:鉴于i,我们知道i & (i >> 1) >= (1<<((int)log2(i)-1)) <=> msb2(i)=1,但log2(i)无法轻松计算。也许这仍然可以以某种方式使用?

4 个答案:

答案 0 :(得分:1)

如果你可以到达最左边的1位(见msb64()),其余的很容易:

#include <stdio.h>
#include <inttypes.h>

uint64_t msb64(uint64_t x);

int main(int argc, char **argv)
{
    uint64_t x, msb;

    if (argv[1] && sscanf (argv[1], "%lu", &x) == 1) {
        msb = msb64(x);
        printf ("msb2(%lu) = %d\n", x, x & (msb >> 1) ? 1 : 0);
    }
    return 0;
}

uint64_t msb64(uint64_t x)
{
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
    x |= (x >> 32);
    return x & ~(x >> 1);
}

$ ./a.out 11
msb2(11) = 0
$ ./a.out 15
msb2(15) = 1

答案 1 :(得分:0)

我相信这个技术上符合标准:写63 if语句(假设你的整数是64位),每个可能的解决方案一个。是的,这基本上模仿了一个循环会做什么,但它仍然是恒定的时间(相应的循环实际上是,但有点难以争辩)。

答案 2 :(得分:0)

Naive log(nbits)事物:(没有循环!)

#include <stdio.h>

int main(int argc, char **argv)
{
unsigned long org , val;

sscanf(argv[1] , "%lx", &org);

val = org ;

if (val >= (2<<16)) val >>= 16;
if (val >= (2<<8)) val >>= 8;
if (val >= (2<<4)) val >>= 4;
if (val >= (2<<2)) val >>= 2;
if (val >= (2<<1)) val >>= 1;
if (val <= 2) val <<= 1; // This is needed for org = 1 ;-)

printf("%lx -> %lx %c\n"
    , org, val, val&1 ? '+' : '-');

return 0;
}

答案 3 :(得分:0)

如果它存在,

clz会使这个变得微不足道。如果没有,您可以按如下方式计算恒定时间。从这个帖子:

http://community.arm.com/thread/6400

static __INLINE uint32_t __CLZ(uint32_t x) {  
    extern uint8_t const log2Lkup[256];  

    if (x >= 0x00010000U) {  
        if (x >= 0x01000000U) {  
            return 8U - log2Lkup[x >> 24];  
        }  
        else {  
            return 16U - log2Lkup[x >> 16];  
        }  
    }  
    else {  
        if (x >= 0x00000100U) {  
            return 24U - log2Lkup[x >> 8];  
        }  
        else {  
            return 32U - log2Lkup[x];  
        }  
    }  
}  

该函数需要在.c文件中定义的log2(二进制对数)查找表:

uint8_t const log2Lkup[256] = {  
  0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U,  
  5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U,  
  6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U,  
  6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U,  
  7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,  
  7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,  
  7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,  
  7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,  
  8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U  
};  

我将代码完整地保留在网站上作为示例,因为修改表格可以使这更容易。

要解决您的情况,请执行(32 - clz(x)) - 1并特别处理x为1或0的情况,因为它未定义。注意:对于64位值,只需添加一些查找...

int wantedValue(int x)
{
    if(x <= 1) return -1;

    return (int)(32 - __CLZ(x)) - 1;
}