byte的取值范围为什么是[-128,127]?
这是个老生常谈的问题了,我们都知道byte
、short
、int
、long
是Java
中8大基本数据类型的整型,但对于他们的取值范围,在初学时,或多或少都有一些疑虑,比如:byte
的取值范围为什么是[-128,127],而不是[-127,127]呢?
要搞清这个问题,首先需要了解几个概念(以8位二进制为例):
机器数
我们都知道计算机只认识0和1,只认识二进制,机器数是数字在计算机中的二进制表现形式,机器数是有符号的,通常符号位放在最高位,0表示正数(+),1表示负数(—)。
因为有符号占据一位,数的形式值就不等于真正的数值,带符号位的机器数对应的数值称为机器数的真值。 例如二进制真值数-0011011,它的机器数为 10011011。
原码
原码(true form)是一种计算机中对数字的二进制定点表示方法。原码=符号位+真值的绝对值。
例如:
+1 = [00000001]
原
-1 = [10000001]
原
0有两种表示方式:
+0 = [00000000]
原
-0 = [10000000]
原
- 优点:简单直观,易于人脑理解。
- 缺点:不能直接带符号位参加运算,可能会出错。例如数学上,1+(-1)=0,而在8位二进制中[00000001]
原
+[10000001]原
=[10000010]原
,换算成十进制为-2。显然出错了。
反码
正数的反码
与原码
相同。
负数的反码
是在其原码
的基础上,符号位
不变,剩下的按位取反,也就是0变1,1变0。
例如:
+1 = [00000001]
原
= [00000001]反
-1 = [10000001]
原
= [11111110]反
0依然有两种表示方式:
+0 = [00000000]
原
= [00000000]反
-0 = [10000000]
原
= [11111111]反
补码
正数的补码
与其原码
相同。
负数的补码
是在其反码
的基础上再加1。
例如:
+1 = [00000001]
原
= [00000001]反
= [00000001]补
-1 = [10000001]
原
= [11111110]反
= [11111111]补
0只有唯一一种表示方式了:
+0 = [00000000]
原
= [00000000]反
= [00000000]补
-0 = [10000000]
原
= [11111111]反
= [00000000]补
解惑
那么现在就可以来回答文章开始的问题了,为什么byte
的取值范围是[-128,127],而不是[-127,127]呢?计算机使用二进制来表示数,最大值是[01111111]表示127,这个没有疑问,最小值不应该是[11111111]表示-127吗?为什么是[10000000]表示-128呢?难道我学的是个假的二进制???
其实不是二进制的问题,只是计算机使用的是补码
来表示数,而不是原码
,至于为什么使用补码
不使用原码
,这里就不细讲了,有兴趣的可以去了解一下。
(-1) + (-127) = [10000001]
原
+ [11111111]原
= [11111110]反
+ [10000000]反
= [11111111]补
+ [10000001]补
= [10000000]补
这就是问题的答案。
[10000000]补
表示-128,是通过补码运算得出来的,但是它并没有原码和反码表示,因为它其实是占了-0的位置,使用[10000000]补
算出原码是错误的。
错误示范:
[10000000]`补` = [01111111]`反` = [00000000]`原`
拓展(byte、short、int、long)
取值范围主要和这些类型存储数据时所占的字节数有关。
- 1字节 = 8位(bit)
byte
: 存储数据占一个字节,取值范围是-128(-2^7) ~ 127(2^7 -1)
short
: 存储数据占两个字节,取值范围是-32,768(-2^15) ~ 32,767(2^15 -1)
int
: 存储数据占四个字节,取值范围是-2,147,483,648(-2^31) ~ 2,147,483,647(2^31 -1)
long
: 存储数据占八个字节,取值范围是-9,223,372,036,854,775,808(-2^63) ~ 9,223,372,036,854,775,807(2^63 -1)