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
. 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
.
Here's an example for the number 6 (0110 in binary):
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?
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.
Subtracting the lowest 1 bit of a value from itself serves as a means to zero out that bit.
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.
It should be portable on any system using two's complement.