使用过Redis的人可能知道,Redis中给我们提供了统计二进制位数为1的位数量的指令bitcount
,JDK中Integer类同样也给我们提供了该功能的方法Integer.bigCount
,得益于此,我们很容易就能一窥该方法的实现
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
上述代码作为Integer类中比较有意思的一个方法,该方法利用了一个技巧:通过分割分配二进制位的方式,CPU可以实现一个指令同时计算多个数值。该方法的前四行都利用了该技巧。
NOTE:CPU要通过分配字节位的方式同时计算多个数值对,需要有一定的前提:由于每个数分配的字节位的长度有限,这就要求计算结果的二进制表示不能超出分配的位数。在当前问题上,显而易见是成立的:相加的两个数的最大值所占的二进制位数只有分配的二进制位的一半,结果值需要的二进制位必然不会超过分配的二进制位数。
案例解析
为了利于问题的解决,对计算二进制位1的数量这个问题,做一个等价转换:计算二进制位上每一位值的和。
以数字1823425321
为例,二进制数值为
0 1 1 0 1 1 0 0 1 0 1 0 1 1 1 1 0 1 0 0 0 0 1 1 0 0 1 0 1 0 0 1
1. 方法第一行
将二进制的每1位都视为一个单独的数字,从左往右两个两个数字配对,形成16组二进制数相加,得到16个数值(2位二进制)。为了使结果是2位二进制数,相加前还需先给每个数前面补零。计算过程如下:
0 1 1 0 1 1 0 0 1 0 1 0 1 1 1 1 0 1 0 0 0 0 1 1 0 0 1 0 1 0 0 1
⇩
00 01 01 00 01 01 01 01 00 00 00 01 00 01