这个位操作在Java中如何工作?
我正在研究Java如何计算int的位集。
在我的脑海里,我有一些像这样的简单(我认为是正确的):
public static int bitCount(int number){ final int MASK = 0x1; int count = 0; for(int i = 0; i >> i) & MASK) == MASK){ count++; } } return count; }
相反,我发现了一种方法,我完全不知道在做什么(对我来说似乎很神奇):
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;
有人可以帮助理解这是什么以及为什么一个简单的function,如我最初想到的那个/可能是坏的?
当然
i = i - ((i >>> 1) & 0x55555555);
5的位模式是0101
(4位),因此掩码是位模式01
的重复16次。 该行计算该数字的每两位对中的位数。 如果考虑两位对, (i >>> 1) & 0x1
得到低位的高位。 现在,有四种可能的两位模式
00 ~> 00 - 00 = 00 01 ~> 01 - 00 = 01 10 ~> 10 - 01 = 01 11 ~> 11 - 01 = 10
现在每个两位对包含原始位置的位数。
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
接下来,我们计算每个四位组(也称为半字节)中的位数。 通过屏蔽具有0x3 = 0011(b)
的半字节,我们得到半字节的低位两位的位数,并且(i >>> 2) & 0x3
从高位的两位得到计数。蚕食。 现在添加了这些计数。 由于每个计数最多为2,因此总和最多为4,因此不会留下半字节。
i = (i + (i >>> 4)) & 0x0f0f0f0f;
现在计算每个八位字节中的位数。 每个半字节包含该位置原始位置的位数。 向右移动四位将计数从每个位置的高阶半字节移动到低阶半字节,这些都被添加。 然后我们还将计数从低阶半字节移动到相邻八位字节的高阶nets,由& 0x0f0f0f0f
屏蔽掉。 由于每个八位字节最多可以设置8位,因此计数不会离开八位字节的低位半字节。
i = i + (i >>> 8);
现在我们添加相邻八位字节对的计数。
i = i + (i >>> 16);
现在我们在高阶两个八位字节和低阶二中添加计数的总数。
return i & 0x3f;
最后,除了最低阶八位字节之外的所有八位字节都被屏蔽掉,因为高阶八位字节仍然包含中间计数。
您的简单实现可能被认为是错误的原因是性能。 复杂的bit-hack使用较少的操作而没有分支,因此速度更快。 然而,这更容易出错。
计算int
(或long
)中的设置位的另一种巧妙方法是
public static int bitCount(int n) { int count = 0; while(n != 0) { ++count; n = n & (n-1); } return count; }
这是有效的,因为n = n & (n-1)
清除n = n & (n-1)
的最后一个设置位并保持其他所有内容不变。 如果n
的位模式结束
...100...0
n-1
的位模式是
...011...1
并且n
中最后1位之前的位是相同的。 在Java中,保证也可以用于负数,因为整数溢出具有环绕语义,因此如果n = Integer.MIN_VALUE
,则位模式为100...0
并且n - 1
变为具有位模式的Integer.MAX_VALUE
011...1
cool方法只有在计算机具有int的有限值范围时才有效。 因为你需要在计算之前知道所有需要的掩码,所以它无法在无限范围(如BigInteger)中轻松工作(以及其他很酷的位算法)。
无论如何,你可以通过以下方式阅读它的工作原理: http : //graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
它在本章的底部。