怎样快速判断掩码第一个为1的Bit位置

本文介绍了在底层软件开发中利用位掩码标识内存块状态的方法,并提供了两种高效查找空闲内存块位置的技术方案:查找表法和位运算法。

原文地址:http://blog.youkuaiyun.com/benny5609/article/details/2194823


在底层软件开发过程中经常使用位掩码标识一个状态符号。举一个例子来说,比如一个U32类型的变量Use_Mask用来表示32个内存块的占用状态,变量的每一位代表一个内存块的使用状态,1b表示空闲,0b表示被占用。当应用程序需要使用一个空闲块时,只需要查询Use_Mask哪一位为1,就可以直接将给Bit位对应的内存块拿来使用了,当然在使用前将该位置1了。同理,使用完给内存块后,也需要将对应位置0就可以了。
那么,如何根据Use_Mask得到为1位的位置呢,比较直接的方法如下:
int GetUnUseMem(U32 UseMask)
{
    int i;
    U32 temp=UseMask;
    
    for(i=0;i<32;i++)
    {
        if(temp &0x00000001)
            return i;
        temp= temp>>1;
    }
    return -1;
}
显然,用这种方法可以得到期望的结果,但是该函数的计算量是与输入参数的内容相关的,最坏情况的运算时间可能是最好情况的32倍。这在一些实时要求比较高的系统中是很难接受的。同时,在函数代码中存在循环,判断分支,也非常不适合当前具有预取指令,乱序执行等功能的处理器提高其性能。当然,我们可以将循环展开,定义寄存器变量等技巧来解决一部分问题,但是是否有其它方法吗。这里考虑了两种方案,抛砖引玉:

1、查找表

该方法一般在掩码长度为8位时比较常见,即定义一个数组X,数组长度为2 的n次方,n为掩码的长度。如掩码为8位时可定义一个256个单元的数组,数组第m个单元初始化为但掩码值为m时其第一个为1位的位置,比如m=0xF4,则11110100b,其第一个为一的位在bit2上,则X[m]=2,同理但X[0xF5]=0,当然X[0x00]=-1。有了这张表,那么查找比特1位置的函数实现很简单:
int X[256]={-1,0,1,0,...}
int GetUnUseMem(U8 UseMask)

{
    return X[UseMask];
}
那么问题是当UseMask为32位时怎么办,难道构建2的32次方长度的数组吗,也即4G个单元,当然不好,除非你要想搞死你的机器。那就分段处理吧。
int GetUnUseMem(U32 UseMask)
{
    int    Ret;
    if((
Ret= X[UseMask&0xFF])>=0) goto GetUnUseMem_End;
    if((Ret= (X[UseMask&0xFF00 >>8] +8))>=0) goto GetUnUseMem_End;
    if((Ret= (X[UseMask&0xFF0000 >>16] +16))>=0) goto GetUnUseMem_End;
    Ret= X[UseMask&0xFF000000 >>24] +24;
GetUnUseMem_End:
    return Ret;
}
如果是64位的掩码呢,最多要判断8次,显然不合适,因此可以使用两级解码,第一级掩码中一位代表下一级掩码的相应8位是否有1。
int X[256]={-1,0,1,0,...}
U8
Y[256]={0,0,8,0,16, 0 ,...}
int GetUnUseMem(U8 UseMask1,U64 UseMask)
{
    register U8 index =
Y[UseMask1];
    index =
(U8)(UseMask2>>index & 0xFF);
    return X[index ];
}

2、位运算

使用位运算可以更快的得到,也不需要用查找表占用内存空间,由于访问内存非常影响处理器的效率,而使用这种方式会使用寄存器或者CPU缓存,不通过北桥再经过DDRII内存控制器得到,因此效率比查找表要高。
int GetUnUseMem(U32 UseMask)
{
    register U32    index =
UseMask ;
    //将第一个为1位的低位都置1,其它位都置0
    index = (index-1)  &  ~index;
    //得到有多少为1的位
    index = index&0x55555555 + (index>>1)&0x55555555; 
    index = index&0x33333333+
(index>>2)&0x33333333;
    index = index&0x0F0F0F0F+ (index>>4)&0x0F0F0F0F;
    index = index&0xFF + (index&0xFF00 >> 8) + (index&0xFF0000 >> 16) + (index&0xFF000000 >> 24);
    //得到位数,如果为32则表示全0
    return (int)(index);
}

<think>好的,我现在需要解决用户的问题:如何用Python判断一个数字中的某一个bit是否为1。首先,我得回忆一下位运算的相关知识。判断特定位是否为1,通常的做法是使用位与运算。假设用户给的是一个整数num,以及要检查的位的位置bit_pos。那么,我应该将num与一个掩码进行按位与操作,这个掩码只有在目标位是1,其他位都是0。例如,如果检查第3位(从0开始计数),掩码就是1左移3位,即0b1000。 如果按位与的结果不是0,就说明那个位是1。否则就是0。所以,逻辑表达式应该是(num & (1 << bit_pos)) != 0。但需要注意,bit_pos是从0开始还是从1开始,用户可能需要明确这一点。 然后,我需要考虑一些边界情况,比如bit_pos是否为负数,或者是否超出了num的位数范围。例如,如果num是一个32位的整数,bit_pos超过31的话,移位可能会导致问题,但在Python中整数的大小没有限制,所以移位超过位数的话可能得到更大的掩码,但按位与的结果可能还是0,这可能需要用户自己处理。 接下来,我需要编写一个函数来实现这个功能。函数的大体结构应该是接受num和bit_pos作为参数,返回布尔值。例如: defis_bit_set(num, bit_pos):return (num& (1 << bit_pos)) !=0 但这里需要确保bit_pos是非负的。可能还需要添加参数检查,如果用户传入负数位,可以抛出异常或者返回False。例如: def is_bit_set(num,bit_pos):if bit_pos< 0:return Falsereturn (num &(1 << bit_pos)) !=0 不过,用户可能希望更严格的检查,比如抛出ValueError。这取决于用户的需求。 然后,我需要给出示例代码,并解释每个步骤的作用。比如,num=5(二进制101),检查第2位是否为1,结果应该是True。再比如,检查第1位是否为1,结果应该是False。另外,用户可能对位运算不熟悉,所以解释掩码的生成和按位与的作用是必要的。例如,1左移bit_pos位生成掩码,与num进行按位与后,如果该位是1,结果就是非零,否则为零。 可能还需要提到,如果bit_pos超过num的二进制位数,例如num是5(二进制101),检查第3位,此时1<<3是8,5&8=0,返回False。这符合预期,因为第三位确实是0。 最后,用户可能想知道如何处理不同的情况,比如从右边还是左边开始计数,或者是否应该从最高位开始。这里默认的位位置是从最低位开始,也就是右边开始,第0位是最右边的位。需要明确这一点,避免误解。 总结一下,解决方案是使用按位与和移位操作,判断结果是否为非零。编写相应的函数,并处理可能的错误输入。</think>要判断数字中某个bit是否为1,可以使用位运算中的`按位与`操作。以下是具体实现代码: ```python def is_bit_set(num, bit_pos): """判断数字的指定位是否为1""" if bit_pos < 0: return False return (num & (1 << bit_pos)) != 0 # 示例用法 num = 5 # 二进制 101 print(is_bit_set(num, 0)) # 检查第0位 → True print(is_bit_set(num, 1)) # 检查第1位 → False print(is_bit_set(num, 2)) # 检查第2位 → True ``` **代码解释**: 1. `1 << bit_pos`:创建一个掩码,将1左移`bit_pos`位(如bit_pos=2得到二进制`100`) 2. `num & mask`:通过按位与运算提取指定位的值 3. `!= 0`:若结果不为0说明该位是1,否则是0 **注意**: - 位位置从0开始计数(最右侧为第0位) - 处理了负数位的边界情况 - 适用于任意大小的整数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值