完美实现按位 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(&) 操作?? @VaibhavBisht0b111
是二进制值,它与十六进制的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
使用&
运算符(按位与)您可以屏蔽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 (~) 运算符(翻转位)的主要内容,如果未能解决你的问题,请参考以下文章