简体   繁体   English

对移位感到困惑(以字节为单位计数位)

[英]Confused About Bit Shifting (Counting Bits in Byte)

I'm trying to count the number of set bits in a byte but I appear to be running into some issues I can't find answers to. 我正在尝试计算一个字节中设置的位数,但似乎遇到了一些无法找到答案的问题。

My code currently looks like 我的代码目前看起来像

   public static int numBits(byte input){
       int count = 0;
       while (input != 0){
           if ((input & 1)==1){
               count++;
           }
           input >>= 1;
       }
       return count;
   }

It appeared to work fine until I tried x = -1 . 在我尝试x = -1之前,它似乎工作正常。

This ended up creating an infinite loop as value 1 bits were being inserted. 由于插入了值1位,最终导致了无限循环。 So I stumbled upon 所以我偶然发现

Java: right shift on negative number Java:负数右移

which to my interpretation meant I should use input >>>= 1; 以我的解释,这意味着我应该使用input >>>= 1; but this still led to an infinite loop. 但这仍然导致无限循环。

So I tried to figure out what was going on by trying 所以我试图通过尝试找出正在发生的事情

byte x = -1;
System.out.println(x >>> 1);
System.out.println(x >> 1);

which lead to 导致

2147483647
-1

leaving me more confused. 让我更加困惑。 Where did the number 2147483647 come from and where might I be making a logic mistake? 数字2147483647是哪里来的,我可能在哪里犯逻辑错误?

The >>> operator means shift to the right, but do not sign-extend . >>>运算符意味着向右移动,但不进行符号扩展

Your trial is pretty close, but you're not actually modifying x , so you can do: 您的试用期已经接近了,但是您实际上并未修改x ,因此您可以执行以下操作:

int x = -1;
x = x >>> 1;
System.out.println(x);
x = x >> 1; // highest bit = 0 now
System.out.println(x);

Which will yield 哪个会产生

2147483647
1073741823

Notice that I'm using int rather than byte here, since the result of bit shifts coerces the input to at least be integer sized. 请注意,我在这里使用int而不是byte ,因为位移的结果将输入强制为至少整数大小。

Interestingly, when you run: 有趣的是,当您运行时:

byte input = -1;
input = (byte) (input >>> 1);

The result is still -1 . 结果仍然-1 This is because of the type coercion happening with this operator mentioned above. 这是因为上述操作符发生类型强制。 To prevent this from affecting you, you can mask the bits of input to make sure you only take the first 8: 为了防止这种情况影响您,您可以屏蔽input的位以确保仅使用前8个:

byte input = -1;
input = (byte) ((input & 0xFF) >>> 1);

Putting this together, if you were to run: 如果要运行,将它们放在一起:

byte input = -1;
input = (byte) ((input & 0xFF) >>> 1);
System.out.println(input);
input = (byte) ((input & 0xFF) >> 1);
System.out.println(input);

You get the expected results: 您会得到预期的结果:

127
63

This is all being caused by the way signed integers are stored as binary values. 这都是由有符号整数作为二进制值存储的方式引起的。 The way the highest bit of the number determines the sign (sort of—zero makes things weird), and the sign was being preserved with the arithmetic shift. 数字的最高位确定符号的方式(某种程度上为零,使事情变得怪异),并且通过算术移位来保留符号。 Your print statements are giving weird results because the results are being promoted to int values instead of bytes. 您的打印语句给出了奇怪的结果,因为结果被提升为int值而不是字节。

If you want a really simple fix for this, you can just use an int to store your value, but make sure to mask off everything but the lowest-order byte: 如果您想对此进行真正简单的修复,则可以使用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;
}

Like I said in my comment above, you should really read about Two's complement representation of signed numbers in binary. 就像我在上面的评论中所说的那样,您应该真正阅读二进制数字的二进制补码表示法。 If you understand how negative numbers are represented, and the difference between arithmetic/logical shifts, all of that will make perfect sense. 如果您了解负数的表示方式以及算术/逻辑移位之间的差异,那么所有这些都将是非常合理的。

You might find how the JVM actually does it is interesting. 您可能会发现JVM实际上是如何做到的。 Note: there is only 8 bits so you don't really need a loop. 注意:只有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;
}

NOTE: On the x86/x64, the JVM replaces this with an intrinsic. 注意:在x86 / x64上,JVM将其替换为内部函数。 ie it uses a single machine code instruction. 即它使用单个机器代码指令。 If you copy this method and compare it will calling the original it is 3x slower as the JVM only replaces a hard coded list of methods, so it will replace Integer.bitCOunt, but not a copy. 如果您复制此方法并进行比较,它将调用原始方法,它的速度要慢3倍,因为JVM仅替换了硬编码的方法列表,因此它将替换Integer.bitCOunt,而不是副本。

In short, use the built in method instead of re-inventing it. 简而言之,请使用内置方法而不是重新发明它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM