首先理解几个概念:机器数、真值、原码、反码、补码。
- 机器数:一个数在计算机中的二进制表现形式。机器数是带符号的,即分正负,第一位代表符号,0:正;1:负。
- 真值:就是在机器数的基础上将表示符号的0和1替换成+、-。
- 原码:可以理解为就是机器数。只不过首位不看成是符号位。
- 反码:正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余位取反。
- 补码:正数的补码是其本身,负数的补码是在其反码的基础上加一。
举例说明(8位)
| 十进制 | 原码 | 反码 | 补码 |
|---|---|---|---|
| +1 | 0000 0001 | 0000 0001 | 0000 0001 |
| -1 | 1000 0001 | 1111 1110 | 1111 1111 |
解释为什么byte的取值范围是-128~127
因为原码是包含符号位的,所以它能表示的最大的数是2的7次方减1。也就是127。讲道理的话负数理应也是-127才对。为什么能到-128呢?
计算机可以有三种编码方式表示一个数。对于正数来说,三种编码方式是一样的
1 = 0000 0001(原) = 0000 0001(反) = 0000 0001(补)
对于负数
-1 = 1000 0001(原) = 1111 1110(反) = 1111 1111(补)
原码, 反码和补码是完全不同的。
机器数是有符号位的,正常情况下是根据符号位对真值区进行加减运算,但是我们要让计算机的电路板设计足够简单,而且对于减法来说,减去一个数实际上相当于加上这个数的负数1 - 1 = 1 + (-1)。这样计算机就只需要计算加法而无需判别符号位了。
如果用原码表示, 让符号位也参与计算,如下:
1 - 1 = 1 + (-1) = 0000 0001(原) + 1000 0001(原) = 1000 0010(原) = -2
显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数。
反码
为了解决原码做减法的问题, 出现了反码:
1 - 1 = 1 + (-1) = 0000 0001(原) + 1000 0001(原) = 0000 0001(反) + 1111 1110(反) = 1111 1111(反) = 1000 0000(原) = -0
发现用反码计算减法, 结果的真值部分是正确的。 而唯一的问题其实就出现在”0”这个特殊的数值上。 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的。 而且会有[0000 0000]原和[1000 0000]原两个编码表示0。
补码
于是补码的出现, 解决了0的符号以及两个编码的问题:
1 - 1 = 1 + (-1) = 0000 0001(原) + 1000 0001(原) = 0000 0001(补) + 1111 1111(补) = 0000 0000(补) = 0000 0000(原) = 0
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:
(-1) + (-127) = 1000 0001(原) + 1111 1111(原) = 0111 1111(补) + 0000 0001(补) = 1000 0000(补) = 1000 0000(原) = -0
可以用-0来表示-128.实际上-128并没有原码或者反码可以表示。 使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127]。