为什么一个byte的存储范围是-128~127?
文本关键字:byte、字节、二进制位、反码、补码
一、byte
在计算机中,一个二进制位是最小的存储单元,由于是二进制,所以能存储的数字只能是0和1。显然,如果我们直接去操作每个二进制位将是很麻烦的过程,所以在编程中我们直接使用的是其他的数据类型,如:byte、int、float。这些数据类型能够使我们的数据存储更加方便,我们只需要关心他们能够存储多大范围和什么样类型的数据就可以了。 那么一个byte,也就是我们所说的一字节,他所占用的空间是8个二进制位。
- 1 byte = 8 bit(比特)
这8个bit就是8个二进制位,其中有一个是符号为,刚好可以用0和1来代表正负。那么这8个二进制位到底能够表示多大范围的数字呢?对于正数的进制转换相信难不倒大家,也可以参考底部的相关文章,我们先来看一下负数在二进制下是如何表示和转换的。
二、反码与补码
首先把公式立在这里:
- 正数的补码 = 原码 = 反码
- 负数的补码 = 反码 + 1
那么首先什么是原码呢?很简单,在我们不考虑符号的情况下,按照进制的转换方法将一个十进制数转换为二进制数,再添加上相应的符号位就完成了。符号位出现在最前(左)面一位,0代表正数,1代表负数。
- +3 -> 11 -> 根据符号和byte长度补全:0000 0011
- -5 -> 101 -> 根据符号和byte长度补全:1000 0101
那么为什么会提出反码和补码的概念呢?有两个原因:
1. 反码
保证在二进制下能够正常的进行正负数间的运算。
首先我们来看一下如果直接使用原码存储,在进行正负数运算的时候会出现什么样的情况。5+(-3)=2,那么在二进制下的运算也和十进制一样,直接相加,该进位进位,但是符号位没法直接参与运算,换句话说,让计算机直接判断算式最后的正负其实比较困难。 原码计算:0000 0011 + 1000 0101 = 1000 1000,结果是:-8(不需要纠结最后的符号位应该取什么,因为在计算机中并没有采用这种方法进行计算,只是举例)。显然,直接采用原码计算的这种方式在正数下是没问题的,但是在负数时就不适用了,所以我们需要重新定义一个规则对负数进行处理。 由于在正数下计算是没问题的,那么就可以规定:正数的反码等于原码,负数的反码为除去符号位,其他取反。
- +3 -> 原码:0000 0011 -> 反码:0000 0011
- -5 -> 原码:1000 0101 -> 反码:1111 1010
反码计算后:1111 1101 -> 原码:1000 0010 -> 十进制:-2。嗯,好像没什么问题了,但是当一个正数和一个负数的运算结果为正数(如:+5和-3,大家可以自己验证)或者恰好为0时还是会有问题。
2. 补码
+0和-0的冲突问题。
从相反数的概念我们可以知道,一个正数肯定存在一个与之对应的相反数,对于整数来说我们只要直接改变一下符号位就行了。But!这个0就很特殊,有一个耳熟能详的概念:0的相反数还是0,这会直接导致进制的转换不是一一对应的关系了。
- +2 -> 原码:0000 0010 -> 反码:0000 0010
- -2 -> 原码:1000 0010 -> 反码:1111 1101
反码计算后:1111 1111 -> 原码:1000 0000 -> 十进制:-0。结果貌似正确,但是在正数中:0000 0000也同样代表0。那么对于1000 0000,是不能直接被抹去的,那就让它来代表一个特殊的数字吧:-128。 其实,特殊的不只是这一个数字,如对于Java中的short,占用两个字节,最高一位为符号位,那么就会出现这个数字:1000 0000 0000 0000,从原码上看也是-0,对于int类型也是一样,那么这个问题就可以总结为:符号位为1,其他位均为0,我们应该怎么处理。 于是,为了解决这一类问题,就有了补码的概念:负数的补码 = 反码 + 1。我们用临界位置的计算来进行举例,如:(-2)+(-126)=-128。
- -2 -> 原码:1000 0010 -> 反码:1111 1101 -> 补码:1111 1110
- -126 -> 原码:1111 1110 -> 反码:1000 0001 -> 补码:1000 0010
- 补码运算结果:1000 0000(该类数字没有原码和反码)
到此我们该敲个黑板,划个重点了:在内存中都是使用补码来进行计算的(整数类型)!
三、byte的数据范围
明确了上面几个概念,那么byte的范围应该就很清楚了。
- 最大的正数:0111 1111 -> 2^8 - 1 = 127
- 最大的负数:1000 0000 -> -2^8 = -128
对于其他正数类型的范围也是如此:
- Java中的short:2字节 -> -2^16 ~ 2^16 - 1
- Java中的int:4字节 -> -2^32 ~ 2^32 - 1
- Java中的long:8字节 -> -2^64 ~ 2^64 - 1
不同语言定义的数据类型在内存中占用的字节数是不同的,而且也不需要直接记这些范围,一般来说记字节就好。