为什么会出这个系列呢?
平时有很多碎片化时间,比如下班的地铁上,或者等待的时间,我们总喜欢拿出手机玩,这个时间也可以用来学习呢,佳爷最近想考考软件设计师,所以把自己准备的过程记录下来,也希望在碎片时间可以复习。
今天来说说数据表示这块,主要包括以下内容:
上述概念大概了解之后,再来说说原码、反码、补码以及如何计算。
原码
原码是一种计算机中对数字的二进制定点表示法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为 0,负数该位为 1(0 有两种表示: 0 和 -0),其余位表示数值的大小。举个例子,用 8 位二进制表示一个数, 12 的原码为 00001100,-12 的原码就是 10001100。
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[ 1]原 = 0000 0001 [-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111]
即
[-127 , 127] 原码是人脑最容易理解和计算的表示方式.
反码
一个数字用原码表示是容易理解的,但是需要单独一个位来表示符号位,并且在进行加法时,计算机需要先识别某个二进制原码是正数还是负数,识别出来之后再进行相应的运算。这样效率不高,能不能让计算机在进行运算时不用去管符号位,也就是让符号位参与运算。要实现这个功能,就要用到反码。
反码的表示方法是:正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[ 1] = [00000001]原 = [00000001]反 [-1] = [10000001]原 = [11111110]反 可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.
反码是一种在计算机中数的机器码表示。对于单个数值(二进制的 0 和 1)而言,对其进行取反操作就是将 0 变为 1,1 变为 0。正数的反码和原码一样,负数的反码就是在原码的基础上符号位保持不变,其他位取反。
十进制 | 原码 | 反码 |
---|---|---|
6 | 0000 0110 | 0000 0110 |
-3 | 1000 0011 | 1111 1100 |
下面来看一下,用反码直接运算会是什么情况,以 6 - 3
为例,6 - 3
等价于 6 (-3)
。
6 - 3 ==> 6 (-3)
0000 0110 // 6(反码)
1111 1100 // -3(反码)
----------------------
0000 0010 // (反码)
0000 0010 // 2(原码)
很明显通过反码进行 6 (-3)
加法运算时,输出值比预期值差了一个 1。接着再来看下 1 (-1)
的运算结果:
1 - 1 ==> 1 (-1)
0000 0001 // 1(反码)
1111 1110 // -1(反码)
----------------------
1111 1111 // (反码)
1000 0000 // -0(原码)
由上可知 1 (-1)
的运算结果为 -0
,而预期的值是 0
。继续看个示例 0 0
:
0 0 ==> 0 0
0000 0000 // 0(反码)
0000 0000 // 0(反码)
----------------------
0000 0000 // (反码)
0000 0000 // 0(原码)
这里可以知道 -0 对应的原码是 1000 0000
,而 0 对应的原码是 0000 0000
。虽然 -0 和 0 代表的数值是一样的,但是在用原码和反码表示时它们是不同的。通过以上的多个示例,发现使用反码进行加法运算并不能保证得出正确的结果。原因是用一个字节表示数字的取值范围时,这些数字中多了一个 -0。为了解决反码出现的问题,就出现了补码。
补码
补码是一种用二进制表示有符号数的方法。正数和 0 的补码就是该数字本身。负数的补码则是将其对应正数按位取反再加 1。补码系统的最大优点是可以在加法或减法处理中,不需因为数字的正负而使用不同的计算方式。只要一种加法电路就可以处理各种有符号数加法,而且减法可以用一个数加上另一个数的补码来表示,因此只要有加法电路和补码电路即可以完成各种有符号数加法和减法,在电路设计上相当方便。
补码的表示方法是: 正数的补码就是其本身,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后 1. (即在反码的基础上 1)
[ 1] = [00000001]原 = [00000001]反 = [00000001]补 [-1] = [10000001]原 = [11111110]反 = [11111111]补 对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.
另外,补码系统的 0 就只有一个表示方式,这和反码系统不同(在反码系统中,0 有两种表示方式),因此在判断数字是否为 0 时,只要比较一次即可。下图是一些 8 位补码系统的整数,它可表示的范围包括 -128 到 127,总共 256 个整数。
既然说补码可以解决反码在运算中遇到的问题,继续以 6 (-3)
为例来验证一下这个结论。
十进制 | 原码 | 反码 | 补码 |
---|---|---|---|
6 | 0000 0110 | 0000 0110 | 0000 0110 |
-3 | 1000 0011 | 1111 1100 | 1111 1101 |
6 (-3)
以补码形式的计算过程如下:
6 - 3 ==> 6 (-3)
0000 0110 // 6(补码)
1111 1101 // -3(补码)
----------------------
0000 0011 // 3(补码)
很明显这时得到了正确的结果,那么再来看一下以补码形式计算 1 - 1
的计算过程:
1 - 1 ==> 1 (-1)
0000 0001 // 1(补码)
1111 1111 // -1(补码)
----------------------
0000 0000 // 0(补码)
参考文章:
https://segmentfault.com/a/1190000021511009
https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html