(数字)和(-数字)的含义

meaning of (number) & (-number)

本文关键字:数字      更新时间:2023-10-16

(number) & (-number)是什么意思?我已经搜索过它,但找不到含义

我想在 for 循环中使用i & (-i),例如:

for (i = 0; i <= n; i += i & (-i))

假设 2 的补码(或i是无符号的),-i等于~i+1

i & (~i + 1)是提取最低设置位i的技巧。

它之所以有效,是因为 +1 实际所做的是设置最低清除位,并清除低于该位的所有位。因此,在i~i+1中设置的唯一位是i中的最低设置位(即~i中的最低清除位)。低于~i+1的位在 中是清晰的,高于i~i之间的位是不相等的。

在循环中使用它似乎很奇怪,除非循环体修改i,因为i = i & (-i)是一个幂等操作:做两次会再次得到相同的结果。

[编辑:在其他地方的评论中,您指出代码实际上是i += i & (-i)。因此,对于非零i,它的作用是清除i的最低一组设置位,并设置高于该位的下一个清除位,例如 101100 -> 110000。对于没有高于最低设定位(包括i = 0)的明确位的i,它将i设置为 0。因此,如果不是因为i0开始,每个循环都会增加i至少是前一个循环的两倍,有时甚至更多,直到最终它超过n并中断或进入0并永远循环。

在没有注释的情况下编写这样的代码通常是不可原谅的,但根据问题的领域,这可能是一个"明显"的值序列来循环。

假设负值使用 2 的补码。然后-number可以计算为(~number)+1,翻转位并加1。

例如,如果number = 92.那么这就是它在二进制中的样子:

number               0000 0000 0000 0000 0000 0000 0101 1100
~number              1111 1111 1111 1111 1111 1111 1010 0011
(~number) + 1        1111 1111 1111 1111 1111 1111 1010 0100
-number              1111 1111 1111 1111 1111 1111 1010 0100
(number) & (-number) 0000 0000 0000 0000 0000 0000 0000 0100

从上面的例子中可以看出,(number) & (-number)给你的位最少。

您可以看到在 IDE One 上在线运行的代码:http://ideone.com/WzpxSD

下面是一些 C 代码:

#include <iostream>
#include <bitset>
#include <stdio.h>
using namespace std;
void printIntBits(int num);
void printExpression(char *text, int value);
int main() {
int number = 92;
printExpression("number", number);
printExpression("~number", ~number);
printExpression("(~number) + 1", (~number) + 1);
printExpression("-number", -number);
printExpression("(number) & (-number)", (number) & (-number));
return 0;
}
void printExpression(char *text, int value) {
printf("%-20s", text);
printIntBits(value);
printf("n");
}
void printIntBits(int num) {
for(int i = 0; i < 8; i++) {
int mask = (0xF0000000 >> (i * 4));
int portion = (num & mask) >> ((7 - i) * 4);
cout << " " << std::bitset<4>(portion);
}
}

这里还有一个 C# .NET 中的版本:https://dotnetfiddle.net/ai7Eq6

我想我只是花点时间展示一下它是如何工作的。此代码为您提供最低设置位的值:

int i = 0xFFFFFFFF; //Last byte is 1111(base 2), -1(base 10)
int j = -i;         //-(-1) == 1
int k = i&j;        //   1111(2) = -1(10) 
// & 0001(2) =  1(10)
// ------------------
//   0001(2) = 1(10). So the lowest set bit here is the 1's bit

int i = 0x80;       //Last 2 bytes are 1000 0000(base 2), 128(base 10)
int j = -i;         //-(128) == -128
int k = i&j;        //   ...0000 0000 1000 0000(2) =  128(10) 
// & ...1111 1111 1000 0000(2) = -128(10)
// ---------------------------
//   1000 0000(2) = 128(10). So the lowest set bit here is the 128's bit
int i = 0xFFFFFFC0; //Last 2 bytes are 1100 0000(base 2), -64(base 10)
int j = -i;         //-(-64) == 64
int k = i&j;        //   1100 0000(2) = -64(10) 
// & 0100 0000(2) =  64(10)
// ------------------
//   0100 0000(2) = 64(10). So the lowest set bit here is the 64's bit

它对无符号值的工作方式相同,结果始终是最低设置位的值。

给定您的循环:

for(i=0;i<=n;i=i&(-i))  

没有设置位 (i=0),因此您将为此操作的增量步骤返回 0。因此,除非修改n=0i,否则这个循环将永远持续下去。

运算i & -i用于隔离相应整数的最低有效非零位。

  • 在二进制表示法中,num可以表示为a1b,其中a表示最后一位之前的二进制数字,b表示最后一位之后的零。

  • -num等于(a1b)¯ + 1 = a¯0b¯ + 1b由所有零组成,因此由所有零组成。

    • -num = (a1b)¯ + 1=>a¯0b¯ + 1=>a¯0(0…0)¯ + 1=>¯0(1…1) + 1=>a¯1(0…0)=>a¯1b
  • 现在,num & -num=>a1b & a¯1b=>(0..0)1(0..0)

例如,如果 i = 5

| iteration | i | last bit position | i & -i|
|-------- |--------|-------- |-----|
| 1    | 5 = 101     | 0 | 1 (2^0)|
| 2    | 6 = 110     | 1 | 2 (2^1)|
| 3    | 8 = 1000    | 3 | 8 (2^3)|
| 4    | 16 = 10000  | 4 | 16 (2^4)|
| 5    | 32 = 100000 | 5 | 32 (2^5)| 

此操作主要用于二叉索引树中上下移动树

PS:由于某种原因,堆栈溢出将表视为代码:(