为什么要有原码、反码、补码?
1、原码
原码:十进制数据的二进制表现形式,最左边是符号位,0为正,1为负
一个字节 8 个bit,第 1 位是符号位, 0 为正, 1 为负,后 7 位表示数值。
那么最大值应该为符号位是 0 表示正数,其他位都为 1,为 0111 1111,为十进制的 127;最小值应该是符号位是 1 表示负数, 其他位都为 1,为 1111 1111,为十进制的 -127。
原码已经可以表示正数和负数,为什么还需要反码和补码?
十进制 | 原码 |
---|---|
3 | 0000 0011 |
2 | 0000 0010 |
1 | 0000 0001 |
0 | 0000 0000 |
-1 | 1000 0001 |
-2 | 1000 0010 |
-3 | 1000 0011 |
在原码的计算过程中,
2 + 1 = 3
<==> 0000 0010 + 0000 0001 = 0000 0011
二进制计算的结果与 2 + 1 = 3 完全一致,正数是完全没有问题的
But
-2 + 1 = -1
<==> 1000 0010 + 0000 0001 = 1000 0011
二进制计算的结果 -2 + 1 结果是 -3,而正确结果应该是 -1
-2 - 1 = -3
<==> 1000 0010 - 0000 0001 = 1000 0001
二进制计算的结果 -2 - 1 结果是 -1,而正确结果应该是 -3
由此可见负数计算会出现错误,计算结果与预期结果是相反的,为了解决负数运算的问题,因此发明了反码
2、反码
负数的原码在计算时总是往相反的方向走,因为有第一位符号位负的存在,加的时候变小,减的时候变大,所以干脆把负数的数据位全部取反,0 变 1、1 变 0,而正数原码计算没有问题,所以正数的反码和原码一样,也就引出了反码的概念
反码:正数的反码是其本身,负数的反码是符号位保持不变,其余位取反
十进制 | 原码 | 反码 |
---|---|---|
3 | 0000 0011 | 0000 0011 |
2 | 0000 0010 | 0000 0010 |
1 | 0000 0001 | 0000 0001 |
0 | 0000 0000 | 0000 0000 |
-0 | 1000 0000 | 1111 1111 |
-1 | 1000 0001 | 1111 1110 |
-2 | 1000 0010 | 1111 1101 |
-3 | 1000 0011 | 1111 1100 |
-2 + 1 = -1
<==> 1111 1101 + 0000 0001 = 1111 1110
计算结果 1111 1110 对应的是 -1 的反码,与实际结果相符
-2 - 1 = -3
<==> 1111 1101 - 0000 0001 = 1111 1100
计算结果 1111 1100 对应的是 -3 的反码,两个负数的运算也与结果相符
But
-1 + 3 = 2
<==> 1111 1110 + 0000 0011 = 0000 0001
计算结果 0000 0001 是 1 的反码,而预期结果是 2,再试试别的数值
-2 + 5 = 3
<==> 1111 1101 + 0000 0101 = 0000 0010
计算结果 0000 0010 是 2 的反码,而预期结果是 3
这些出错的例子都有一些共同的特点,它们都跨越 0 了,这是因为存在 0 和 -0,而 0 和 -0 是一样的,但占据了一位反码的空间,为解决这个问题,引出了补码的概念
3、补码
反码跨 0 计算总是会和预期结果存在 1 的偏差,这个偏差是由于反码多一个 -0 而导致的,解决方法其实很简单,把整个反码向下移一位,也就是消除 -0 这一位, 规定 -1 的补码为 1111 1111,也就是常说的负数的补码是反码加1(所以这个加1并不是无厘头规定非要加1),而正数原码没有任何计算问题,所以正数的补码依然和原码相同
补码:正数的补码是其本身,负数的补码是在其反码的基础上加 1
十进制 | 原码 | 反码 | 补码 |
---|---|---|---|
127 | 0111 1111 | 0111 1111 | 0111 1111 |
… | … | … | … |
3 | 0000 0011 | 0000 0011 | 0000 0011 |
2 | 0000 0010 | 0000 0010 | 0000 0010 |
1 | 0000 0001 | 0000 0001 | 0000 0001 |
0 | 0000 0000 | 0000 0000 | 0000 0000 |
-0 | 1000 0000 | 1111 1111 | |
-1 | 1000 0001 | 1111 1110 | 1111 1111 |
-2 | 1000 0010 | 1111 1101 | 1111 1110 |
-3 | 1000 0011 | 1111 1100 | 1111 1101 |
… | … | … | … |
-127 | 1111 1111 | 1000 0000 | 1000 0001 |
-128 | 无 | 无 | 1000 0000 |
再来计算
-1 + 3 = 2
<==> 1111 1111 + 0000 0011 = 0000 0010
结果 0000 0010 为 2,完美匹配预期,跨 0 问题完美解决
-2 - 1 = -3
<==> 1111 1110 - 0000 0001 = 1111 1101
结果 1111 1101 是十进制的 -3,负数计算问题完美解决
补码成功解决了所有问题,而因为加 1 向下移动一位,多出来一位 1000 0000
,所以规定它为特殊的 -128,没有反码,也没有原码,这就是为什么一个字节的范围正数到 127,而负数可以到 -128
4. 类型转换的底层原理
- byte:1 个字节
- short:2 个字节
- int:4 个字节
- long:8 个字节
1)隐式转换
byte a = 10; //0000 1010
short b = a; //转化为0000 0000 0000 1010
2)强制类型转换
int a = 300; //0000 0000 0000 0000 0000 0001 0010 1100
byte b = (byte)a; //前面直接抹掉 0010 1100
System.out.println(b); //输出 0010 1100 对应的 44
int a = 200; //0000 0000 0000 0000 0000 0000 1100 1000
int b = (byte)a; //前面直接抹掉 1100 1000
System.out.println(b);
//1100 1000 对应的反码是 1100 0111,原码是 1011 1000,输出对应的 -56