java int与byte[]数组的相互转换 补码

来源:互联网 发布:3000多的笔记本知乎 编辑:程序博客网 时间:2024/06/10 16:29

由于需要写了两个将int型与byte[]数组相互转换的方法,将int型转变成byte[]数组还好说,将byte[]数组转换回int就出现问题了。

将int转换成byte[]数组的方法代码如下:

public static byte[] intToBytes(int origin) {byte[] bytes = new byte[4];for (int i = 0; i < 4; i++) {bytes[3 - i] = (byte) (origin >>> (i * 8));}return bytes;}
思路是将int型数据的32位分别存储进4个byte中。对int型做强制转换(byte),会将int型的高24位截掉,只保留低8位。因此将一个整型向右位移8位,然后强制转换成byte型,就能得到一个保存这个整型中间8位的byte数值,以此类推得到4个byte,构成数组。

将byte[]数组转换回int的方法代码如下:

public static int bytesToInt(byte[] bytes) {if (bytes.length != 4)throw new IllegalArgumentException("bytes should be a 4-length byte array");int i = bytes[0];for (int p = 1; p < 4; p++) {i = i << 8;System.out.println(i);i = i | bytes[p];}return i;}

思路是与上一个方法反过来,将每个byte转换成int,得到的int数据只有低8位对应于原byte,高24位全部为0(这是错的),将这个int左移相应位数(8的0、1、2、3倍)得到的新int数据,它对应于目标int型数据的某8位。四个byte对应四个8位,按位或后得到目标int。

测试代码如下:

public static void main(String[] args) throws IOException {int origin = 91825;byte[] bytes = intToBytes(origin);for (int i = 0; i < 4; i++) {System.out.print(bytes[i] + " ");}System.out.println();System.out.println("bytesToInt(bytes)结果:" + bytesToInt(bytes));ByteArrayInputStream bais = new ByteArrayInputStream(bytes);DataInputStream dis = new DataInputStream(bais);System.out.println("readInt()结果:" + dis.readInt());}

结果如下:

0 1 102 -79 
0
256
91648
bytesToInt(bytes)结果:91569
readInt()结果:91825

从结果中可已看出,int型数据91825转变成的byte[]数组,经过DataInputStream的readInt()方法读取后,正确还原成了91825,说明转变成的byte[]数组没有错。

但是bytesToInt()方法的结果却错了。

根据bytesToInt()方法打印的输出可以发现对91825的高24位所对应的3个byte进行还原时都没有错误,只有对它的最低8位构成的byte数值:-79进行还原操作时除了差错。91648 - 79 = 91569。

错误原因:分析了老半天,才发现是数据类型转换的原因。将byte转换成int,得到的int数据并不一定低8位对应于原byte而高24位全部为0,比如这个byte为负数的时候,得到的int是补码:比如这个byte为-79,得到的int就是对-79重新进行补码编码后的int,而不是在高24位上加24个0,它的高24位全部为1(本来就应该这样,但是自己把自己绕进去了没想起来)。

解决办法:那么如何从重新编码后的int中得到理想的int呢,发现这么简单,只要将这个int与0xff按位与就行了。也就是说将bytesToInt改成:
public static int bytesToInt(byte[] bytes) {
if (bytes.length != 4)
throw new IllegalArgumentException(
"bytes should be a 4-length byte array");
int i = bytes[0] & 0xff;
for (int p = 1; p < 4; p++) {
i = i << 8;
System.out.println(i);
i = i | (bytes[p] & 0xff);
}
return i;
}
这样结果就对了。
解决原因:所谓补码就是:1.对正数,用二进制数表示。2.对负数,先求得它的相反数的二进制表示,然后按位取反,最后加1,最高位取1,这样做的原因是由于溢出的原因,可以将减法当成加法算。
比如计算7 - 5:相对于8,5的补数是3(5+3=8),7 - 5 = 7 + (-(8 - 3))= 7 + 3 - 8 = 10 - 8 = 2;
如果我们只保留1位八进制,也就是说我们所能看到的最大的数就是7,7再加1后,由于只保留1位八进制,我们得到的是0,而不是8。那么7 + 3, 由于比8大2,因此看到的就是2,相当于隐式计算了减去8。
而对于计算机中的整数,也是有位数限制的,超过了就要溢出,以byte为例,它用8为二进制存储,超过了255就要溢出,因此负数应该相对于256计算补数。比如-79,它的补数为256 - 79 = 255 - 79 + 1:255是全1的8位byte,用255 - 79,就相当于对正数79的二进制码按位取反;注意,结果要再加1。结果是正数256-79 = 177(这也是为什么对于有符号byte型,大于127的,也就是最高位为1的,都定义为负数,而不是单纯的只是为了跟正数对半分(我是这样认为的,错了请指教))的编码,也是负数-79的补码。这就是补码这样规定的原因(我是这样认为的,错了请指教)。同理-79的相反数的原码 = 256 - (256 - 79)= 255 - (256 - 79 )+1,也就是-79的补码再做补码,即按位取反再加1。
因此,对每个byte所存储的8位,如果最高位为0,则机器理解为正数,转换成int后也只是在高24位全部取0;如果byte的最高位为1,机器理解为负数,先求出负数相反数的原码,转换成int型,再求出该int的补码,即2^32 - (256 - b)(假设b为这个负数byte所对应的二进制码所对应的正数)= (2^32 - 256) + 256 - (256 - b) = (2^32 - 256) +b。 显然2^32 - 256相当于高24位全为1,第8为全为0的整型。也就是说该int的补码就是在b的基础上加了高24位,并且这24位全部取1,而b对应的低8位保持不变。
0xff表示255,即低8位全部为1,高24位全部为0的int型数值。将它与某个int型进行按位与得到就是,这个int第8位保持不变,高24位全部取0。因此与0xff按位与就解决问题了。
操作如此简单,亏了想了好长时间。

	
				
		
原创粉丝点击