如何将最高值1位设置为0,最好是在c ++中

时间:2014-08-05 11:06:40

标签: c++ algorithm bit-manipulation bit

例如,如果我有数字20:

0001 0100

我想将最高值1位(最左边)设置为0。

所以

0001 0100

将成为

0000 0100

我想知道哪种方法最有效。

最好是在c ++中。

我尝试从原始数字中减去两个这样的最大幂,

unsigned long long int originalNumber;
unsigned long long int x=originalNumber;
               x--;
               x |= x >> 1;
               x |= x >> 2;
               x |= x >> 4;
               x |= x >> 8;
               x |= x >> 16;
               x++;
               x >>= 1;

            originalNumber ^= x;

,但我需要更高效的东西。

5 个答案:

答案 0 :(得分:3)

#include <limits.h>

uint32_t unsetHighestBit(uint32_t val) {
    for(uint32_t i = sizeof(uint32_t) * CHAR_BIT - 1; i >= 0; i--) {
        if(val & (1 << i)) {
            val &= ~(1 << i);
            break;
        }
    }
    return val;
}

解释

这里我们取类型uint32_t的大小,即4个字节。每个字节有8位,因此我们从i开始迭代32次,值为31到0。

在每次迭代中,我们将值1移动到i向左,然后按位 - 和(&)将它移动到我们的值。如果返回值!= 0,则设置i处的位。一旦我们找到一个设置的位,我们按位和(&)我们的初始值与设置位的按位求反(~)。

例如,如果我们有数字44,它的二进制表示将是0010 1100。我们找到的第一个设置位是第5位,从而产生掩码0010 0000。该掩码的按位否定为1101 1111。现在,当使用此掩码按位&初始值时,我们得到值0000 1100

在带有模板的C ++中

这是一个如何使用模板在C ++中解决这个问题的例子:

#include <limits>

template<typename T> T unsetHighestBit(T val) {
    for(uint32_t i = sizeof(T) * numeric_limits<char>::digits - 1; i >= 0; i--) {
        if(val & (1 << i)) {
            val &= ~(1 << i);
            break;
        }
    }
    return val;
}

答案 1 :(得分:3)

如果你被限制为8位(如你的例子中所示),那么只需使用任何算法预先计算数组中的所有可能值(byte [256]),或者只需手动输入。

然后你只需查找所需的值:

   x = lookup[originalNumber]

不能比那快得多。 : - )

更新:所以我读错了这个问题。

但是如果使用64位值,则将其分解为8个字节,可以将其分解为一个字节[8]或将其覆盖在一个联合或更聪明的东西中。之后,找到第一个不为零的字节,并按照我上面的回答中的那个特定字节进行操作。我担心效率不高,但最多只有8次测试(平均4.5次)+一次查找。

当然,创建字节[65536}查找会使速度加倍。

答案 2 :(得分:3)

棘手的部分是找到最重要的位,或计算前导零的数量。其他所有东西都可以或多或少地通过左移1(少一个),减1然后否定(构建逆掩码)和&运算符来完成。

众所周知的bit hacks站点有几个实现找到最重要的位的问题,但它也值得研究编译器内在函数,因为所有主流编译器都有一个内在的用于此目的,他们实现和目标架构一样有效(我几年前在x86上使用GCC进行了测试,结果是单指令)。在没有对目标体系结构进行分析的情况下,哪个是最快的是不可能分辨的(代码行数较少,或者更少的汇编指令并不总是更快!)但是,编译器实现这些内在函数并不比你更糟糕是一个公平的假设。能够实施它们,并且可能更快 使用具有某种可理解名称的内在函数也可能比从现在开始看5年后的某些内容更容易理解。

不幸的是,虽然这并非完全不常见,但这并不是您期望在C或C ++库中找到的标准化功能,至少没有我所知道的标准功能。

对于GCC,您正在寻找__builtin_clz,VisualStudio将其称为_BitScanReverse,而英特尔的编译器将其称为_bit_scan_reverse

除了计算前导零之外,您可以查看相同的Bit Twiddling网站所拥有的内容&#34;四舍五入到下一个#2的幂,您只需要跟进右移1,和NAND操作。请注意,站点上给出的5步实现是针对32位整数的,您必须将64位宽值的步数加倍。

答案 3 :(得分:0)

以下代码将关闭最右边的位:

bool found = false;
int bit, bitCounter = 31;
while (!found)  {
    bit = x & (1 << bitCounter);
    if (bit != 0)   {
        x &= ~(1 << bitCounter);
        found = true;
    }
    else if (bitCounter == 0)
        found = true;
    else
        bitCounter--;
}

答案 4 :(得分:-1)

我知道将非正数位设置为0的方法。

a & (a - 1)

来自Book:Warren H.S., Jr. - Hacker's Delight.

您可以反转您的位,将更多权限设置为零并反转。但我现在知道在你的情况下反转位的有效方法。