完美实现按位 NOT (~) 运算符(翻转位)

Posted

技术标签:

【中文标题】完美实现按位 NOT (~) 运算符(翻转位)【英文标题】:Implementing bitwise NOT (~) operator perfectly (flipping the bits) 【发布时间】:2020-06-11 06:58:24 【问题描述】:

最近我在做一些与位操作有关的事情。到目前为止,我已经尝试了许多位操作操作。但我被困在一个操作上。

假设我有 int n = 5;二进制(101),现在我想在这个int上执行按位NOT,我认为结果是(010),而是结果是-6。

但是当我尝试 n = ~(-n) 时,它给了我结果 4(尽管仍然没有得到正确的输出)。请告诉它为什么显示这种行为是因为我的 int 不是无符号的。另外请告诉我实现操作的完美方法,以便我可以获得正确的输出。我的主要动机是正确翻转位。

谢谢

【问题讨论】:

int 有超过 3 位。正如您正确猜测的那样,其中之一恰好是符号位。什么是“实现操作的完美方式”?你想按位而不是自己实现吗?你现在想实现什么操作,什么不完善? 提示:n = 5 包含大量前导零(数量取决于int 的大小)。您正在将输出中的前导零设置为 1。 @churill 我只是想实现像 (101) -> (010) 这样的 NOT 操作 您需要首先找到最重要的设置位,从中构造一个掩码,然后进行否定,最后屏蔽最高位。您的问题不清楚,因此您得到的答案恰好是 3 位,这可能不是您想要的。 @VaibhavBisht - 您必须计算出设置了多少位,然后只翻转这些位。这涉及的不仅仅是~ 运算符。您还需要小心处理符号位(未指定或实现定义的元素 [不记得哪个副手] 对负值的行为)。此外,与~ 不同,这意味着两次应用您的方法不一定会返回原始值(101 -> 010 -> 001)。 【参考方案1】:

int 有多个树位,因此您必须像这样屏蔽按位否定的结果:

int flip(int n) 
    // bitwise AND with 0b111 = 7, this will clear all but the last 3 bits
    return ~n & 0b111;

您得到 -6 的原因是因为 int 通常以二进制补码表示,其中 -6 全部为 1-bits 但以 010 结尾。您必须删除这些前导的1-bits 才能获得正确的结果。

一般来说,我建议不要对有符号数字使用按位运算,而是执行以下操作:

unsigned flip(unsigned n) 
    return ~n & 0b111;


// this version works with any number of bits, not just 3
unsigned flip(unsigned n, unsigned bits) 
    unsigned mask = (1 << bits) - 1;
    return ~n & mask;

如果你不知道你的数字有多少位,你必须首先找到最高有效位。最天真的方式,可以这样:

unsigned log2(unsigned val)

    unsigned result = 0;
    while (val >>= 1) 
        ++result;
    
    return result;


unsigned variable_flip(unsigned n) 
    return flip(n, log2(n));

您可以找到更有效的解决方案here。

例如:

unsigned log2_debruijn(uint32_t val) 
    static const unsigned MultiplyDeBruijnBitPosition[32] = 0, 9,  1,  10, 13, 21, 2,  29, 11, 14, 16, 18, 22, 25, 3, 30,
                                                          8, 12, 20, 28, 15, 17, 24, 7,  19, 27, 23, 6,  26, 5,  4, 31;

    // first round down to one less than a power of 2
    // this step is not necessary if val is a power of 2
    val |= val >> 1;
    val |= val >> 2;
    val |= val >> 4;
    val |= val >> 8;
    val |= val >> 16;

    return MultiplyDeBruijnBitPosition[(val * uint32_t0x07C4ACDD) >> 27];

【讨论】:

什么是 0b111 它不是十六进制值。它是什么?为什么要用它执行 AND(&) 操作?? @VaibhavBisht 0b111 是二进制值,它与十六进制的0x7 或十进制的7 相同。 @Jabberwocky 但为什么我们只使用 7 执行和 (&) 操作 @VaibhavBisht 因为您在问题中没有明确表示您想要一个通用算法来处理可变位宽。 @VaibhavBisht 因为数字 5 有 3 个有效位。这篇文章可能会让你感兴趣:***.com/questions/31393100/…【参考方案2】:

你可能想要这个:

// assuming n is a 32 bit int
n = 5;          // n = 00000000000000000000000000000101
n = ~n;         // n = 11111111111111111111111111111010
n = n & 0x7;    // n = 00000000000000000000000000000010

使用&amp; 运算符(按位与)您可以屏蔽n 的第3 位到第31 位

您当然可以将最后两个语句压缩为一个:

n = ~n & 0x7;

【讨论】:

【参考方案3】:

首先,您需要找到最重要的位。您可以通过右移 1 直到达到零来做到这一点,并计算您移动的次数。

unsigned int bit = 0;
unsigned int tmp = n;
while (tmp) 
    tmp >>= 1;
    bit++;

然后将值 1 向右移动那么多位,得到一个值 n 向上舍入到最接近的 2 次方,然后减去 1 以获得所有位低于该值的掩码:

unsigned int mask = (1U << bit) - 1;

然后将它与您的值进行异或以翻转掩码中设置的位:

n = n ^ mask;

【讨论】:

以上是关于完美实现按位 NOT (~) 运算符(翻转位)的主要内容,如果未能解决你的问题,请参考以下文章

按位运算符简单地翻转整数中的所有位?

在没有按位运算的情况下翻转整数中的位

delphi 按位运算 not and or xor shl shr

运算符

按键^异或运算符

如何翻转 T-SQL 中的位域?