前世今生
众所周知,java是使用补码来表示数字的(不包括字符型),数字的运算也是以补码形式进行的,那么为什么要用补码表示呢?
要了解补码,我们首先要知道计算机可以用原码 反码 补码这三种编码方式表示一个数
先来看看我们是如何定义这三种编码的
- 正数的
原码 反码 补码
都是一样的 - 负数的
反码
是除了符号位其他取反,补码
是在反码
上加1
所以对于正数三码合一没啥可说的,这里主要来聊一下负数,因为计算机在进行计算的时候,并不会像人脑一样识别原码的第一位为符号位,进而会去根据符号位对真值进行加或减。所以我们要让计算机的运算变的尽可能的简单,比如两个数相减我们可以看成是加上一个负数,并且让符号位也参与运算
- 原码的减法
10 + (-9) = 0000 1010(原) + 1000 1001(原) = 1001 0011(原) = -19
// 正解应该是 1, -19显然是错误的
例子中正解应该是 1
, -19
显然是错误的。这就引申出了反码,负数的反码是除了符号位,其他位取反
- 反码的减法
10 + (-9) = 0000 1010(原) + 1000 1001(原) = 0000 1010(反) + 1111 0110(反) = 0000 0001(反) = 0000 0001(原)
// 结果是正确的 = 1
看似反码貌似解决了符号位参与运算的问题,其实不然,让我们再看一个特殊的例子
1 + (-1) = 0000 0001(原) + 1000 0001(原) = 0000 0001(反) + 1111 1110(反) = 1111 1111(反) = 1000 0000(原) = -0
// ps: 反码相加符号位参与运算,且若最高位相加后产生进位,最后得到的结果要加1
// 结果看似是正确的,但是显然不符合我们的预期,0 和 -0 应该是一个数字
于是补码就这样出现了
- 补码的减法
1 + (-1) = 0000 0001(原) + 1000 0001(原) = 0000 0001(反) + 1111 1110(反) = 0000 0001(补) + 1111 1111(补) = 0000 0000(原) = 0
// ps: 补码运算最高位进位会舍去
// 结果完全正确 且 -0 不存在了
还有一点需要注意的是 1000 0000(补)
并没有相应的原码和反码,仅用来表示 -128
最后再附张幼儿班常用数字进制表 <( ̄▽ ̄)/
2进制 | 10 进制 | 16 进制 |
---|---|---|
0000 0001 | 1 | 0x1 |
0000 0010 | 2 | 0x2 |
0000 0011 | 3 | 0x3 |
0000 0100 | 4 | 0x4 |
0000 0101 | 5 | 0x5 |
0000 0110 | 6 | 0x6 |
0000 0111 | 7 | 0x7 |
0000 1000 | 8 | 0x8 |
0000 1001 | 9 | 0x9 |
0000 1010 | 10 | 0xa |
0000 1011 | 11 | 0xb |
0000 1100 | 12 | 0xc |
0000 1101 | 13 | 0xd |
0000 1110 | 14 | 0xe |
0000 1111 | 15 | 0xf |
0001 0000 | 16 | 0x10 |
0001 0100 | 20 | 0x14 |
0001 1110 | 20 | 0x1e |
0100 0000 | 64 | 0x40 |
0111 1111 | 127 | 0x7f |
1111 1111 | 255 | 0xff |