简体   繁体   English

i+=(i&-i) 做什么? 便携吗?

[英]What does i+=(i&-i) do? Is it portable?

Let i be a signed integer type.i成为有符号整数类型。 Consider考虑

i += (i&-i);
i -= (i&-i);

where initially i>0 .其中最初i>0

  1. What do these do?这些有什么作用? Is there an equivalent code using arithmetic only?是否有仅使用算术的等效代码?
  2. Is this dependent on a specific bit representation of negative integers?这是否取决于负整数的特定位表示?

Source: setter's code of an online coding puzzle (w/o any explanation/comments).来源:在线编码谜题的 setter 代码(没有任何解释/评论)。

The expression i & -i is based on Two's Complement being used to represent negative integers.表达式i & -i基于用于表示负整数的二进制补码 Simply put, it returns a value k where each bit except the least significant non-zero bit of i is set to 0 , but that particular bit keeps its own value.简单地说,它返回一个值k ,其中除i的最低有效非零位之外的每一位都设置为0 ,但该特定位保持其自己的值。 (ie 1 ) (即1

As long as the expression you provided executes in a system where Two's Complement is being used to represent negative integers, it would be portable.只要您提供的表达式在使用Two's Complement表示负整数的系统中执行,它就是可移植的。 So, the answer to your second question would be that the expression is dependent on the representation of negative integers.所以,回答你的第二个问题是,该表达依赖于负整数的表示。

To answer your first question, since arithmetic expressions are dependent on the data types and their representations, I do not think that there is a solely arithmetic expression that would be equivalent to the expression i & -i .要回答您的第一个问题,由于算术表达式取决于数据类型及其表示形式,因此我认为不存在等同于表达式i & -i的单独算术表达式。 In essence, the code below would be equivalent in functionality to that expression.本质上,下面的代码在功能上等同于该表达式。 (assuming that i is of type int ) Notice, though, that I had to make use of a loop to produce the same functionality, and not just arithmetics. (假设iint类型)但是请注意,我必须使用循环来产生相同的功能,而不仅仅是算术。

int tmp = 0, k = 0;
while(tmp < 32)
{
    if(i & (1 << tmp))
    {
        k = i & (1 << tmp);
        break;
    }
    tmp++;
}
i += k;

On a Two's Complement architecture, with 4 bits signed integers:在二进制补码架构上,具有 4 位有符号整数:

|  i |  bin | comp | -i | i&-i | dec |
+----+------+------+----+------+-----+
|  0 | 0000 | 0000 | -0 | 0000 |   0 |
|  1 | 0001 | 1111 | -1 | 0001 |   1 |
|  2 | 0010 | 1110 | -2 | 0010 |   2 |
|  3 | 0011 | 1101 | -3 | 0001 |   1 |
|  4 | 0100 | 1100 | -4 | 0100 |   4 |
|  5 | 0101 | 1011 | -5 | 0001 |   1 |
|  6 | 0110 | 1010 | -6 | 0010 |   2 |
|  7 | 0111 | 1001 | -7 | 0001 |   1 |
| -8 | 1000 | 1000 | -8 | 1000 |   8 |
| -7 | 1001 | 0111 |  7 | 0001 |   1 |
| -6 | 1010 | 0110 |  6 | 0010 |   2 |
| -5 | 1011 | 0101 |  5 | 0001 |   1 |
| -4 | 1100 | 0100 |  4 | 0100 |   4 |
| -3 | 1101 | 0011 |  3 | 0001 |   1 |
| -2 | 1110 | 0010 |  2 | 0010 |   2 |
| -1 | 1111 | 0001 |  1 | 0001 |   1 |

Remarks:评论:

  1. You can conjecture that i&-i only has one bit set (it's a power of 2) and it matches the least significant bit set of i .您可以推测i&-i只设置了一位(它是 2 的幂)并且它匹配i的最低有效位集。
  2. i + (i&-i) has the interesting property to be one bit closer to the next power of two. i + (i&-i)有一个有趣的特性,它更接近 2 的下一个幂。
  3. i += (i&-i) sets the least significant unset bit of i . i += (i&-i)设置的至少显著未设定位i

So, doing i += (i&-i);所以,做i += (i&-i); will eventually make you jump to the next power of two:最终会让你跳到下一个二的幂:

| i | i&-i | sum |     | i | i&-i | sum |
+---+------+-----+     +---+------+-----+
| 1 |    1 |   2 |     | 5 |    1 |   6 |
| 2 |    2 |   4 |     | 6 |    2 |  -8 |
| 4 |    4 |  -8 |     |-8 |   -8 |  UB |
|-8 |   -8 |  UB |

| i | i&-i | sum |     | i | i&-i | sum |
+---+------+-----+     +---+------+-----+
| 3 |    1 |   4 |     | 7 |    1 |  -8 |
| 4 |    4 |  -8 |     |-8 |   -8 |  UB |
|-8 |   -8 |  UB |

UB: overflow of signed integer exhibits undefined behavior. UB:有符号整数溢出表现出未定义的行为。

Here is what I researched prompted by other answers.这是我研究的其他答案提示的内容。 The bit manipulations位操作

i -= (i&-i);   // strips off the LSB (least-significant bit)
i += (i&-i);   // adds the LSB

are used, predominantly, in traversing a Fenwick tree .主要用于遍历Fenwick 树 In particular, i&-i gives the LSB if signed integers are represented via two's complement .特别是,如果有符号整数通过二进制补码表示,则i&-i给出 LSB。 As already pointed out by Peter Fenwick in his original proposal, this is not portable to other signed integer representations.正如Peter Fenwick在他最初的提议中已经指出的那样,这不能移植到其他有符​​号整数表示。 However,然而,

i &= i-1;      // strips off the LSB

is (it also works with one's complement and signed magnitude representations) and has one fewer operations.是(它也适用于一个补码和有符号幅度表示)并且少了一个操作。

However there appears to be no simple portable alternative for adding the LSB.然而,似乎没有简单的便携式替代方案来添加 LSB。

If i has unsigned type, the expressions are completely portable and well-defined.如果i有无符号类型,则表达式是完全可移植且定义良好的。

If i has signed type, it's not portable, since & is defined in terms of representations but unary - , += , and -= are defined in terms of values.如果i有符号类型,则它不可移植,因为&是根据表示定义的,而一元-+=-=是根据值定义的。 If the next version of the C++ standard mandates twos complement , though, it will become portable, and will do the same thing as in the unsigned case.但是,如果C++ 标准下一版本强制要求二进制补码,它将变得可移植,并且将执行与未签名情况相同的事情。

In the unsigned case (and the twos complement case), it's easy to confirm that i&-i is a power of two (has only one bit nonzero), and has the same value as the lowest-place bit of i (which is also the lowest-place bit of -i ).在无符号情况下(和二进制补码情况),很容易确认i&-i是 2 的幂(只有一位非零),并且与i的最低位具有相同的值(这也是-i的最低位)。 Therefore:所以:

  • i -= i&-i; clears the lowest-set bit of i .清除i的最低设置位。
  • i += i&-i; increments (clearing, but with carry to higher bits) the lowest-set bit of i .递增(清除,但进位到更高位) i的最低设置位。

For unsigned types there is never overflow for either expression.对于无符号类型,任何一个表达式都不会溢出。 For signed types, i -= i&-i overflows taking -i when i initially has the minimum value of the type, and i += i&-i overflows in the += when i initially has the max value of the type.对于有符号的类型, i -= i&-i溢出服用-ii最初具有该类型的最小值, i += i&-i中溢出的+=i最初具有该类型的最大值。

i & -i is the easiest way to get the least significant bit (LSB) for an integer i . i & -i是获取整数i的最低有效位 (LSB) 的最简单方法。
You can read more here .您可以在此处阅读更多内容。
A1: You can read more about 'Mathematical Equivalents' here . A1:您可以在此处阅读有关“数学等效项”的更多信息。
A2: If the negative integer representation is not the usual standard form (ie weird big integers), then i & -i might not be LSB. A2:如果负整数表示不是通常的标准形式(即奇怪的大整数),那么i & -i可能不是 LSB。

The easiest way to think of it is in terms of the mathematical equivalence:考虑它的最简单方法是根据数学等价性:

-i == (~i + 1)

So -i inverts the bits of the value and then adds 1 .所以-i反转值的位,然后加上1 The significance of this is that all the lower 0 bits of i are turned into 1 s by the ~i operation, so adding 1 to the value causes all those low 1 bits to flip to 0 whilst carrying the 1 upwards until it lands in a 0 bit, which will just happen to be the same position as the lowest 1 bit in i .这样做的意义在于, i所有低0位都被~i操作变成了1 ,因此将1添加到该值会导致所有这些低1位翻转为0同时将1向上传送,直到它落在 a 0位,恰好与i最低的1位相同。

Here's an example for the number 6 (0110 in binary):以下是数字 6(二进制 0110)的示例:

i = 0110
~i == 1001
(~i + 1) == 1010
i & (~i + 1) == 0010

You may need to do each operation manually a few times before you realise the patterns in the bits.在您意识到位中的模式之前,您可能需要手动执行几次每个操作。

Here's two more examples:这里还有两个例子:

i = 1000
~i == 0111
(~i + 1) == 1000
i & (~i + 1) == 1000

i = 1100
~i == 0011
(~i + 1) == 0100
i & (~i + 1) == 0100

See how the + 1 causes a sort of 'bit cascade' carrying the one up to the first open 0 bit?看看+ 1如何导致一种“位级联”,将一个位传送到第一个打开的0位?


So if (i & -i) is a means of extracting the lowest 1 bit, then it follows that the use cases of i += (i & -i) and i -= (i & -i) are attempts to add and subtract the lowest 1 bit of a value.因此,如果(i & -i)是提取最低1位的一种方法,那么i += (i & -i)i -= (i & -i)是尝试添加和减去值的最低 1 位。

Subtracting the lowest 1 bit of a value from itself serves as a means to zero out that bit.从值本身减去值的最低 1 位用作将该位归零的方法。

Adding the lowest 1 bit of a value to itself doesn't appear to have any special purpose, it just does what it says on the tin.将值的最低 1 位添加到自身似乎没有任何特殊目的,它只是按照它在锡上所说的那样做。


It should be portable on any system using two's complement.它应该可以在任何使用二进制补码的系统上移植。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM