我正在尝试计算一个字节中的设置位数,但我似乎遇到了一些我找不到答案的问题。
我的代码目前看起来像
public static int numBits(byte input){
int count = 0;
while (input != 0){
if ((input & 1)==1){
count++;
}
input >>= 1;
}
return count;
}
在我尝试x = -1
之前,似乎工作正常。
当插入值1位时,最终创建了一个无限循环。所以我偶然发现了
Java: right shift on negative number
这对我的解释意味着我应该使用input >>>= 1;
,但这仍然导致无限循环。
所以我试着通过尝试
来弄清楚发生了什么byte x = -1;
System.out.println(x >>> 1);
System.out.println(x >> 1);
导致
2147483647
-1
让我更加困惑。数字2147483647
来自何处以及我可能在哪里制造逻辑错误?
答案 0 :(得分:4)
>>>
运算符表示向右移动,但不签名扩展。
您的试用版非常接近,但您实际上并没有修改x
,所以您可以这样做:
int x = -1;
x = x >>> 1;
System.out.println(x);
x = x >> 1; // highest bit = 0 now
System.out.println(x);
哪个会产生
2147483647
1073741823
请注意,我在这里使用int
而不是byte
,因为位移的结果强制输入至少是整数大小。
有趣的是,当你跑步时:
byte input = -1;
input = (byte) (input >>> 1);
结果是仍然 -1
。这是因为上面提到的这种算子发生了类型强制。为了防止这种情况影响到您,您可以屏蔽input
的位以确保您只占用前8位:
byte input = -1;
input = (byte) ((input & 0xFF) >>> 1);
如果您要跑步,请将它们放在一起:
byte input = -1;
input = (byte) ((input & 0xFF) >>> 1);
System.out.println(input);
input = (byte) ((input & 0xFF) >> 1);
System.out.println(input);
您获得了预期的结果:
127
63
答案 1 :(得分:3)
这完全是由有符号整数存储为二进制值的方式引起的。数字的最高位确定符号的方式(零的排序使得事情变得奇怪),并且通过算术移位保留了符号。你的print语句给出了奇怪的结果,因为结果被提升为int
值而不是字节。
如果你想要一个非常简单的修复方法,你可以使用int
来存储你的值,但一定要屏蔽除最低位字节之外的所有内容:
public static int numBits(byte inByte){
int count = 0;
int input = inByte & 0xFF;
while (input != 0){
if ((input & 1)==1){
count++;
}
input >>= 1;
}
return count;
}
就像我在上面的评论中所说,你应该真正阅读有关二进制签名数字的Two's complement表示。如果你理解如何表示负数,以及算术/逻辑移位之间的差异,那么所有这些都将非常有意义。
答案 2 :(得分:1)
您可能会发现JVM实际上是如何做到这一点很有趣。注意:只有8位,所以你真的不需要循环。
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;
}
注意:在x86 / x64上,JVM将其替换为内在函数。即它使用单个机器代码指令。如果你复制这个方法并比较它将调用原始方法它会慢3倍,因为JVM只替换硬编码的方法列表,所以它将替换Integer.bitCOunt,但不能替换副本。
简而言之,使用内置方法而不是重新发明它。