一次byte变量运算
b3的值为多少?
byte b1 = 127;
byte b2 = 1;
byte b3 = (byte)(b1 + b2);
结果为-128。
对于无符号位的byte类型变量可以表示的数值范围为0 ~ 255(0 ~ 2^8-1),可以表达的数值的个数是256个,即2^8个。
有符号位则为-128 ~ 127(-2^7 ~ 2^7-1),且最高位(第八位)通过0为正数,1为负数来表示数值的正负。可以表达的数值的个数也是256个,即2^8个。
正常来说,127的二进制为0111 1111,1的二进制为0000 0001。相加之后应为1000 0000,即128,为什么是-128?
这也说明在java中默认是有符号位存储变量值的。也应当如此,除非手动声明类似 unsigned 这样的变量修饰符。那么为什么会得出-128呢?先再次复习一下补码的知识。
补码
如何推算补码,例如:
-
127(正数原反补码都一致)
- 原码: 0111 1111
- 反码: 0111 1111
- 补码: 0111 1111
-
-1
- 原码: 1000 0001
- 反码: 1111 1110 (除符号位都取反)
- 补码: 1111 1111 (反码+1)
可以发现一个规律,可以直接对负数的绝对值取反+1得出补码,因为绝对值一定为正数,最高位一定为0,取反时直接对全位数取反结果不会出现问题。
如何通过补码推出实际表示的值(对上面的操作步骤逆操作即可,只演示负数):
例:-1
- 补码: 1111 1111
- 反码: 1111 1110
- 原码: 1000 0001
为什么要使用补码
知道了补码的推算后,那么为什么要使用这种方式?见下面的例子。
-1 + 1
1000 0001
0000 0001
---------
1000 0010 (-2)
-2 + 2
1000 0010
0000 0010
---------
1000 0100 (-4)
正常的进制运算,结果是不正确的。但我们将其都转为补码后:
-1 + 1
1111 1111
0000 0001
---------
0000 0000 (0)
-2 + 2
1111 1110
0000 0010
---------
0000 0000 (0)
-3 + 2
1111 1101
0000 0010
---------
1111 1111 (-1)
结果正确了。为什么会这样?这就是精妙之处,利用进位溢出。当正常的进制运算无法得出正确结果时,不妨换个角度思考,例:
-1 + 1
.... ....
0000 0001
---------
0000 0000 (0)
-1的进制码为多少时,结果为0呢:1111 1111
1111 1111如何得出的呢:绝对值的进制码取反+1
回顾问题
为什么最后的结果是-128,可以推算:
127 + 1
0111 1111
0000 0001
---------
1000 0000
补码1000 0000实际对应的值是多少呢?
补码: 1000 0000
反码: 1111 1111
原码: 1000 0000 (-128)
感觉好像不太对,1000 0000是不是为-0?为什么是-128?详细可以看链接,下面引入文章片段:
0 111 1111 127
0 000 0010 2
0 000 0001 1
0 000 0000 0
1 111 1111 -1
1 111 1110 -2
1 000 0001 -127
1 000 0000 -128
在原码和反码表示法中,0有两种表示方式,分别为正0(0000 0000)和负0(1000 0000/1111 1111)。然而在正常的算数中,0是不应该有符号的。
补码表示法则没有这种区分,不论正0还是负0都会表示为0000 0000(负0取反加1丢掉溢出位后也是这个结果),所以补码正好能用1000 0000多表示一个数。
根据上述补码表格中“递减”的规律,把1000 0000放在-127的后面,表示-128正合适(1000 0000 + 0111 1111 = 1111 1111 = -1),也符合符号位为1表示负数的惯例。