【问题标题】:Why left shifting in java changing the sign value为什么在java中左移改变符号值
【发布时间】:2016-02-24 08:42:17
【问题描述】:

我正在研究java。 我想知道为什么 java 会产生这个输出。 我在这里分享代码。

public class vvn {

    public static void main(String[] args)
    {
        byte [] arr = new byte[4];
        arr[0] = (byte)157;
        arr[1] = 1;
        arr[2] = 0;
        arr[3] = 0;
        System.out.format("read 0x%x 0x%x 0x%x 0x%x \n",arr[3],arr[2],arr[1],arr[0]);
        int v = (arr[0] | (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24));
        System.out.format("read 0x%x\n",v);

    }

}

我得到的输出为

read 0x0 0x0 0x1 0x9d 
read 0xffffff9d

我预计输出应该是 0x0000019d

【问题讨论】:

  • 来自 java doc byte:byte 数据类型是一个 8 位有符号二进制补码整数。它的最小值为 -128,最大值为 127(含)。 byte 数据类型可用于在大型数组中节省内存,其中内存节省实际上很重要。它们也可以用来代替 int ,它们的限制有助于澄清您的代码;变量的范围有限这一事实可以作为一种文档形式。
  • 基本上我是从一个文件中读取的,使用数据输入流每 1 个字节读取 4 次。有什么办法可以做到这一点。我优先考虑字节序
  • @wn 读取一个整数然后调用Integer.reverseBytes(x);

标签: java byte-shifting


【解决方案1】:

您正在从字节(有符号 8 位)转换为整数(有符号 32 位)。最高有效位(最左边的一位)持有符号(请参阅two's complement)。

您的157 是二进制的10011101。由于您将此值分配给 有符号字节(java 没有无符号字节),因此这实际上是一个负数,-99

现在,当您从 byte 转换为 int 时,会保留该值。在负数的情况下,这意味着将所有位设置到左侧以保持符号性。在您的情况下,10011101 变为 11111111 11111111 11111111 10011101

无论如何,在 java 中使用无符号字节是一场噩梦。基本上,您需要像这样用 0xff 屏蔽所有内容(以切断“左侧的”):

int v = ((arr[0] & 0xff) |
    ((arr[1] & 0xff) << 8) |
    ((arr[2] & 0xff) << 16) |
    ((arr[3] & 0xff) << 24));

很漂亮,不是吗?

更新1:另外,你可能对Guava的UnsignedBytes感兴趣...

更新 2: Java 8 Byte 具有 toUnsignedInt() 和 toUnsignedLong() 方法。因此,您的计算变为:

int v = (Byte.toUnsignedInt(arr[0]) |
    (Byte.toUnsignedInt(arr[1]) << 8) |
    (Byte.toUnsignedInt(arr[2]) << 16) |
    (Byte.toUnsignedInt(arr[3]) << 24));

【讨论】:

    【解决方案2】:

    看。 你的表达(arr[1] 等于 Ox100。 所以,你需要额外的 Ox9d,十进制是 157。不幸的是,字节 diapasone 是 [-128; 127]。所以,不可能从字节中得到 157。你的 a[0] 等于-99

    【讨论】:

      【解决方案3】:

      因为最左边的数字是为符号保留的,所以当你向左移动时,当你提交溢出时符号会改变。

      【讨论】:

      • 我怀疑“当你向左移动时,标志会改变”是否属实。
      • 当我们向左移动时,我认为标志不会改变。
      • 为了完整而修改了我的答案。
      【解决方案4】:

      在 Java 中,byte 类型是从 -128 到 127 的有符号值。这意味着 (byte)157 导致溢出,实际上等于 -99。当您输出字节的十六进制值时并不明显,因为-99(有符号)和157(无符号)的二进制表示是相等的(格式转换%x 将该值解释为无符号值)。

      但是,一旦在表达式中使用它,byte 的值就会提升为 int。而 -99 作为 int 的十六进制表示是0xffffff9d。您可以通过在程序中添加以下行来看到这一点:

      System.out.format("0x%x \n", (int)arr[0]);
      

      【讨论】:

        【解决方案5】:

        我认为问题在于 java 中的按位运算是在 ints 上执行的 所以当你使用

        arr[0] | ...
        

        arr[0] 被转换为int。所以不是预期的0x9d,而是0xffffff9d 所以解决你需要&amp;0xff掩码的问题,例如

        int v = ((arr[0] & 0xff) | ((arr[1] & 0xff) << 8) | ...;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-31
          • 2019-01-09
          相关资源
          最近更新 更多