位运算技巧

1. 判断奇偶

if (x & 1 == 0) {  // 7   ... 0111 & 1 == ... 0001
    // 偶数
} else {
    // 奇数
}

2. 高位清零、翻转

如想将一个数只保留最后四位。则让他与仅后四位为1的数字做与运算。 如下,留下来的就是x的后四位

x & 15 // x &  ...0000 1111

那同样,将一个数后四位设为1就是用或运算。如下

x | 15 // x | ...0000 1111

那同样,翻转一个数的某几位就是用异或运算(相同为0,相异为1)。如下

x ^ 15 // x ^ ...0000 1111

针对情况与,还有个小技巧。如果想让一个数后几位清零。可以使用取反运算符。因为取反优先级更高。

x & ~15 // x & 1111... 1111 0000

3. 交换两个数

借用异或将两个数进行交换,是一个经典中的经典。如下

public void swap(int x, int y) {
        x ^= y;
        y ^= x;
        x ^= y;
}

image

4. 位移说明

因为计算机用补码运算,所以正数无论是有符号、无符号位移结果都一致。

1. << x 左移

将一个数的二进制左移x位,末位用0填充。在移动的位数上1没有溢出到符号位之前,可以视为乘以2的x次方

但如果涉及到符号位变动或高位溢出,则不能视为乘法。如下结果为 -2147483648

1073741824 << 1 // 0100 0000... << 1

2. >> x 右移

将一个数的二进制右移x位,正数高位用0填充,负数用1填充。 正数没到0时可看作除以2的x次方

如下

-10 >> 2 // 1000 0000... 1010 >> 2


1000 0000... 1010 -> 1111 1111... 0101 -> 1111 1111... 0110 (原码到补码)

1111 1111... 1101 (右移2位) -> 1111 1111... 1100 -> 1000 0000... 0011 = -3

3. <<< x 无符号左移

逻辑同左移

4. >>> x 无符号右移

这里和右移有区别,无符号右移无论正负,高位都是拿0填充。

如下 -10经过无符号右移后变成了 1073741821

-10 >>> 2 // 1000 0000... 1010 >>> 2

1000 0000... 1010 -> 1111 1111... 0101 -> 1111 1111... 0110 (到补码)
0011 1111... 1101 -> 0011 1111...