java位运算和字节编码(一)

我们都知道计算机存储的是二进制,长度是8个比特。

byte b = 10;
int i = -10;
long l = 10L;

那么以上几个变量如何用二进制表示呢?
很多人第一印象肯定是 b变量占用一个字节,也就是8位,所以 b用二进制表示就是 1010,高位补零,所以是 00001010。那么i是int,占用4个字节,也就是32位,但因为是负数,所以最高位是100000000 00000000 00000000 00001010。同理 l变量则是 00000000 00000000 00000000 00000000 000000000 00000000 00000000 00001010。

上面的推理过程中存在一处错误,就是在java中存储的是补码,而不是原码。正数的原码,反码和补码相同。负数则不是一样。以上面 int i = -10 作为例子。

变量 i
原码 1000 1010
# 反码是符号位不变,其他位取反
反码 1111 0101
#补码则是在反码的基础上加1
补码 1111 0110

所以-10 在计算机中正确的表示应该是 1111 0110。
上面我们已经学习原码,反码和补码相关的概念,至于为什么要用补码,感兴趣的可以自己去查。

我们也知道在网络传输中,存储的就是二进制相关的byte数组。那么现在我们需要往消息中写入int或者long相关的信息,如何转换为byte数组中的相关项?第一个想到的办法就是强制转换。

int x = 135;
System.out.println((byte)x);  /** 输出-121 **/

x = -135;
System.out.println((byte)x); /** 输出-121 **/

可见输出的答案并不如你所愿。想知道为什么这样吗?感兴趣的可以自己去推导。
额,还是帮你们推导一遍吧。我们以 -135为例。
首先我们知道 int 占用4个字节,而byte占用1个字节,同时你也看到 -135已经超出了一个字节所能表示的范围[-128, 127]。如果强制转换后还能显示出正确结果,那才恐怖啊。

-135

原码 10000000 00000000 00000000 10000111

反码 11111111 11111111 11111111 01111000

补码 11111111 11111111 11111111 01111001

我们知道java中存储的是补码,可是因为byte只占一个字节,所以转换的时候只取到最低位那个字节也就是 01111001作为转换后补码存在。因为正数的原码和补码相同,也即是这个字节的原码是01111001,原值是 121。

刚才我们讨论的是int强制转换成byte类型可能存在的问题。那么如果我需要把byte类型的强制转换成int类型的会出现什么样的问题。

byte num = 120;        
System.out.println((int)num); 
num = -120;
System.out.println((int)num);

大家看到这两行示例代码时。尽量自己尝试推导出结果来,不要看我下面的推导过程。
我们以-120作为例子,尝试推导下(看到没有,我喜欢负数)。

-120

原码 11111000

反码 10000111

补码 10001000

##此刻开始转换为int类型,占4个字节,符号位1,用1填充高位三个字节。如果符号位是0,高位会用0填充。

补码 11111111 11111111 11111111 10001000 /** 存储在内存中的int整形补码 **/
反码 11111111 11111111 11111111 10000111
原码 10000000 00000000 00000000 01111000 
原值 -120

所以 当 byte num = -120 时, (int)num = -120
我相信很多人都没有自己推导,直接看我的推导的。那么再给你个机会,尝试推导下当byte num = 120的时候推导过程。

通过byte强制转换成int 好像一切都很顺利,也没有int强制转换成byte相关的问题。
当真的是那样吗?下一篇我们继续探讨这个话题。

原文链接 http://segmentfault.com/a/1190000003758605/

    原文作者:位运算
    原文地址: https://segmentfault.com/a/1190000003758605
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞