for循环的增量语句中的奇数位运算符

Odd bit operator in the increment statement of a for loop

本文关键字:运算符 语句 循环 for      更新时间:2023-10-16

给定循环:

for(++i; i < MAX_N; i += i & -i)

它应该是什么意思?语句i += i & -i完成了什么?

这个循环经常在二进制索引树(或BIT)实现中观察到,它对更新对数时间中的范围或点以及查询范围或点很有用。这个循环有助于根据索引中的设置位选择合适的bucket。有关更多详细信息,请考虑从其他来源阅读有关BIT的信息。在下面的文章中,我将展示这个循环如何帮助基于最低有效集位找到合适的桶。


2s互补有符号系统(当i有符号时)
i & -i是一个比特破解,可以快速找到应该添加到给定数字上的数字,使其尾部比特为0(这就是为什么bit的性能是对数的)。当你对2s互补系统中的一个数字取反时,你会得到一个反向模式中的位加上1的数字。当你加上1时,所有低有效位都会开始反转,只要它们是1(原始数中是0)。遇到的第一个0比特(原始i中的1)将变为1

当您同时使用i-i时,只有该位(最低有效1位)将保持设置,所有较低有效(右侧)位将为zero,较高有效位将为原始编号的inverse

Anding将产生2数的幂,当该幂与数i相加时将清除最低有效设置位。(根据BIT要求)

例如:

i = 28
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
+---+---+---+---+---+---+---+---+
                      *
-i
+---+---+---+---+---+---+---+---+
| 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
+---+---+---+---+---+---+---+---+
  I   I   I   I   I   S   Z   Z
Z = Zero
I = Inverted
S = Same
* = least significant set bit
i & -i
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+---+---+---+---+---+---+---+---+
Adding
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
+---+---+---+---+---+---+---+---+
                      x
x = cleared now


无符号(当i为无符号时)
它甚至适用于1s complementary system或任何其他表示系统,只要iunsigned,原因如下:

-i的值为2sizeof(unsigned int)*CHAR_BITS-i。因此,最低位置位右边的所有位将保持zero,最低位也将保持零,但之后的所有位由于进位位而被反转。

例如:

i = 28
     +---+---+---+---+---+---+---+---+
     | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
     +---+---+---+---+---+---+---+---+
                           *
-i
   0   1   1   1   1   1   <--- Carry bits
     +---+---+---+---+---+---+---+---+
   1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
     +---+---+---+---+---+---+---+---+
     +---+---+---+---+---+---+---+---+
   - | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
     +---+---+---+---+---+---+---+---+
   ----------------------------------------
     +---+---+---+---+---+---+---+---+
     | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
     +---+---+---+---+---+---+---+---+
       I   I   I   I   I   S   Z   Z
Z = Zero
I = Inverted
S = Same
* = least significant set bit
i & -i
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+---+---+---+---+---+---+---+---+
Adding
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
+---+---+---+---+---+---+---+---+
                      x
x = cleared now


无比特破解的实现

您也可以在gcc上使用i += (int)(1U << __builtin_ctz((unsigned)i))

现场示例

相同的非模糊版本是:

/* Finds smallest power of 2 that can reset the least significant set bit on adding */
int convert(int i) /* I purposely kept i signed as this works for both */
{
    int t = 1, d;
    for(; /* ever */ ;) {
        d = t + i; /* Try this value of t */
        if(d & t) t *= 2;  /* bit at mask t was 0, try next */
        else break; /* Found */
    }
    return t;
}

编辑
从此答案添加:

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

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

它之所以有效,是因为+1实际上所做的是设置最低的清除位,并清除所有低于该值的位。所以唯一设置的位i和~i+1都是来自i的最低集位(即,最低清除~i)中的位。低于该值的位在~i+1中被清除,并且比它高的比特在i和~i之间是不相等的。