for循环的增量语句中的奇数位运算符
Odd bit operator in the increment statement of a for loop
给定循环:
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
或任何其他表示系统,只要i
是unsigned
,原因如下:
-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之间是不相等的。
- 多个If语句与使用逻辑运算符计算条件的单个语句的比较
- 三元运算符在返回语句中给出意外的结果
- C++使用逗号运算符的多个控制语句
- 当返回语句时,逗号运算符、大括号初始化列表和 std::unique_ptr 组合在一起
- 是否可以在C++中使用三元运算符在 if 语句中选择比较运算符?
- 在同一语句中重载运算符+多次
- 复制构造函数和复制赋值运算符是否应具有相同的语句?
- c++ 如何计算赋值运算符(if 语句)
- 为什么使用 and 运算符比较 if 语句中的 2 个对象会抛出错误,而使用 2 if 语句则不会
- 在返回语句中嵌套条件运算符
- 为什么 if 语句对于运算符重载函数计算 false
- 使用 if (? 运算符) 语句传递带有指针的数组
- 如何在COUT语句中使用移动运算符
- 在 c++ 中,如何修改运算符以便在同一语句中连续调用它两次
- 我们可以在if语句中使用三元运算符
- 如何修复"运算符不匹配=="比较if语句中的字符串时
- 如何像if语句一样使用三元运算符
- 关于手术室 ( || )运算符和返回语句
- 赋值运算符和条件语句
- 返回语句中的C++11显式转换运算符/构造函数