理解Java字节

时间:2010-10-02 13:05:33

标签: java binary byte

所以在昨天的工作中,我不得不编写一个应用程序来计算AFP文件中的页面。所以我拂去了我的MO:DCA规范PDF并找到了结构化字段BPG (Begin Page)及其3字节标识符。应用程序需要在AIX机器上运行,所以我决定用Java编写它。

为了获得最大效率,我决定读取每个结构化字段的前6个字节,然后跳过字段中的剩余字节。这会让我:

0: Start of field byte
1-2: 2-byte length of field
3-5: 3-byte sequence identifying the type of field

所以我检查字段类型并增加一个页面计数器,如果它是BPG,如果不是,我就不会。然后我跳过字段中的剩余字节而不是通读它们。在这里,跳过(实际上是字段长度)是我发现Java使用带符号字节的地方。

我做了一些谷歌搜索,发现了很多有用的信息。当然,最有用的是执行按位&0xff以获取unsigned int值的指令。这对我来说是必要的,以获得可用于计算要跳过的字节数的长度。

我现在知道,在128,我们从-128开始向后计数。我想知道的是按位运算在这里是如何工作的 - 更具体地说,我是如何得到负数的二进制表示。

如果我正确理解按位&,则结果等于只设置两个数字的公共位的数字。所以假设byte b = -128,我们会:

b & 0xff // 128

1000 0000-128
1111 1111 255
---------
1000 0000 128

那么我怎样才能到达1000 0000 -128?如何获得不太明显的二进制表示,如-72或-64?

6 个答案:

答案 0 :(得分:18)

为了获得负数的二进制表示,你计算二的补码:

  • 获取正数的二进制表示
  • 反转所有位
  • 添加一个

让我们以-72为例:

0100 1000    72
1011 0111    All bits inverted
1011 1000    Add one

因此-72的二进制(8位)表示为10111000

实际发生的情况如下:您的文件有一个值为10111000的字节。当解释为无符号字节(可能是你想要的)时,这是88。

在Java中,当此字节用作int时(例如因为read()返回int,或者因为隐式提升),它将被解释为带符号的字节,并符号扩展为{{ 1}}。这是一个值为-72的整数。

通过与11111111 11111111 11111111 10111000进行AND运算,您只保留最低的8位,因此您的整数现在为0xff,即88。

答案 1 :(得分:2)

  

我想知道的是按位运算在这里是如何工作的 - 更具体地说,我是如何得到负数的二进制表示。

负数的二进制表示是对应的正数进行比特翻转,并加1。此表示称为two's complement

答案 2 :(得分:1)

我猜这里的神奇之处在于字节存储在一个更大的容器中,可能是一个32位的int。如果该字节被解释为有符号字节,则它将被扩展为表示32位int中的相同数字,即如果该字节的最高有效位(第一个)是1,那么在32位int中该1左边的位也变为1(这是由于负数表示的方式,两个补码)。

现在,如果你用& 0xFF那个int切断了那些1并最终得到一个表示你读过的字节值的“正”int。

答案 3 :(得分:1)

不确定你真正想要的:)我假设你问的是如何提取有符号的多字节值?首先,看看签名扩展单个字节时会发生什么:

byte[] b = new byte[] { -128 };
int i = b[0];
System.out.println(i); // prints -128!

因此,符号正确地扩展到32位而不做任何特殊操作。字节1000 0000正确地扩展到1111 1111 1111 1111 1111 1111 1000 0000。 您已经知道如何通过与0xFF进行AND运算来抑制符号扩展 - 对于多字节值,您只希望将最高有效字节的符号扩展为extendet,并将您想要视为无符号的较低有效字节视为假设网络字节order,16位int值):

byte[] b = new byte[] { -128, 1 }; // 0x80, 0x01
int i = (b[0] << 8) | (b[1] & 0xFF);
System.out.println(i); // prints -32767!
System.out.println(Integer.toHexString(i)); // prints ffff8001

您需要禁止除最重要字节之外的每个字节的符号扩展,因此要将带符号的32位int提取为64位长:

byte[] b = new byte[] { -54, -2, -70, -66 }; // 0xca, 0xfe, 0xba, 0xbe
long l = ( b[0]         << 24) |
         ((b[1] & 0xFF) << 16) |
         ((b[2] & 0xFF) <<  8) |
         ((b[3] & 0xFF)      );
System.out.println(l); // prints -889275714
System.out.println(Long.toHexString(l)); // prints ffffffffcafebabe

注意:在基于intel的系统上,字节通常以相反的顺序存储(最低有效字节优先),因为x86架构按此顺序在内存中存储较大的实体。许多x86原创软件也以文件格式使用它。

答案 4 :(得分:0)

要获得无符号字节值,您可以。

int u = b & 0xFF;

int u = b < 0 ? b + 256 : b;

答案 5 :(得分:0)

对于设置了第7位的字节:

unsigned_value = signed_value + 256

使用字节计算数学时,计算模256.有符号和无符号之间的区别在于您为等价类选择不同的代表,而作为位模式的基础表示对于每个等价类保持相同。这也解释了为什么加法,减法和乘法与位模式具有相同的结果,无论您是使用有符号还是无符号整数进行计算。