I would go with n & -n
or with n & (~n + 1)
, in case you are worried about running across one's complement arithmetic, given the latter works with both arithmetics.
E.g.,
> 144 & (~144 + 1)
< 16
Now a short explanation.
The bitwise NOT (i.e., ~
operator) of a number n
gives -(n + 1)
. It inverts all the bits of n
. The number 2 is represented by 00000010
while its negation is 11111101
which equals to -3 (i.e.,
, see the two's complement representation of signed numbers).
Do not to confuse it with logical negation.
E.g., ~144 = -(144 + 1) = -145
.
The bitwise AND (i.e., &
operator) compares two bits of the inputs and generates a result of 1 if both are 1, otherwise it returns 0.
Now the main topic.
This is an old tricks that gives the highest power of 2 that n
is divisible by. This means that it returns a number with a single one bit, specifically the bottom bit that was set in n
.
For example the binary representation of 144 is 010010000
. Its bottom 1 bit is the bit in fourth position (counting backward from right and starting at position 0). Thus the higher power of 2 that divides 144 is 16 (i.e., 00010000
).
144 & (~144 + 1) = 144 & -144 = 16
16 & ( ~16 + 1) = 16 & - 16 = 16
10 & ( ~10 + 1) = 10 & - 10 = 2
12 & ( ~12 + 1) = 12 & - 12 = 4
11 & ( ~11 + 1) = 11 & - 11 = 1
3 & ( ~ 3 + 1) = 3 & - 3 = 1
Note that if n
is not divisible by any power of 2 it returns 1.
Why it works?
The negative of n
is produced by inverting its bits via ~
, then adding 1 (again, see two's complement definition). This sum causes every 1 bit (starting from the bottom) to overflow until a 0 bit is encountered (let us call it the bit x). Here the overflow process stops, leaving remaining bits (those beyond the current x bit) unchanged. Thus performing &
between n
and its inverse will result in a binary string containing only the x bit.
An example follows.
010010000 | +144 ~
----------|-------
101101111 | -145 +
1 |
----------|-------
101110000 | -144
101110000 | -144 &
010010000 | +144
----------|-------
000010000 | 16