数据在计算机中以二进制串存储,这种01序列叫“机器数”。
每个机器数都有对应的值,比如0001换算就是现实里十进制的1,这个值叫它的“真值”。
1.原码
为了表示负数的前面的符号,一种办法是采用“最高一位表示符号而非数值”的编码方式——原码。
如八位的运算器,机器数0000 0001的真值为1,1000 0001的真值为-1。第一位为符号位,剩下的位表示真值。
解决了符号表示问题,但是引出了新的问题,那就是1000 0001不能按照常识意义上来换算了,按正常二进制转十进制的换算,1000 0001实际上代表129。
这样在硬件电路设计时会带来麻烦,要让机器能够先辨识符号位,再进行真值的计算。
2.反码
任何减法都可以转化为加上一个负数,如 1+1 = 1+(-1)。做加法比减法简单,要是能用一种新的编码方法把-1表示出来,然后和+1做加法,最后结果按照这种编码方式还是正确的,那就太好了。于是为了让符号位也能参与计算,反码诞生了。
反码的表示方法基于原码,最高位仍然是符号位。正数的反码是其本身,负数的反码符号位不变,剩下的位全部取反。
+1 原码0000 0001,反码 0000 0001
-1 原码1000 0001,反码1111 1110
人们发现,采用这种编码方式,算出来的值是正确的。
用反码计算:
1-1 = 1+(-1) = 0000 0001 + 1111 1110 = 1111 1111
而1111 1111的反码是1000 0000,即 -0。
采用反码的编码方式看似解决了符号位参与运算的问题,但仍存在一个明显的问题,那就是0的表示不唯一。
0000 0000代表 +0,1000 0000代表 -0,这是自原码就有的问题,是没有意义的。
你会发现——
用原码表示,8位表示的机器数的范围是 [1111 1111, 0111 1111]即[-127,127]。
[-127,127]按现实意义理解的话其实只有255个数,而8个位能理论上表示2^8 = 256个数,少掉的那一位就是用来表示0的正负号了。
3.补码
人们希望有一种更好的编码方式,既能解决符号参与运算的问题,又能让0的表示唯一,由此诞生了补码。
补码是一种基于取模同余的模运算系统的编码方法,这样说有点复杂,实际上用钟表来理解很容易。
时钟的最上方从0开始,顺时针依次为1,2,3……直到12。12同时也是0,时钟表示了0到11总共12个数。
在这个系统中,计算这一行为就可以抽象为拨动指针。
比如我想做加法计算,如1+1,自然等于2,那也可以理解为:指针在1,我顺时针拨一格,停在了2。减法运算1-1,就是时针在1,逆时针拨一格,结果为0。
加法就是顺时针,减法就是逆时针,最后指针停在的位置就是结果。
那么,从6拨到4,有两种拨法,要么顺时针拨10格,要么逆时针拨2格(当然顺时针拨22格也是可以的,也就是多转一圈)。即 6+10 和 6-2 的结果是等价的。或者说在一个计算式中, +10 和 -2 是等价的。
6 + 10 = 16,6 - 2 = 4,何以等价呢?只要让:16对12取模(16除以12得出的余数),结果就是4了。
该规律对于系统内的任何加减运算都是适用的,通过取模同余的方式,把减法转换成了加法,即 -2 可以通过 +10 再对 12 取模来计算。
-1 = +11 mod 12
-2 = +10 mod 12
-3 = +9 mod 12
不难发现,左边负数的绝对值和右边整数的和总是12,时钟是一个模12运算系统。
我们称 -1 的补码是 +11 、-2 的补码是 +10、-3的补码是+9,即一个负数的补码是模运算系统的模减去它的绝对值。
有了补码的概念,1-1 的意思就可以转述为 1 加上 1 的补码,即正数减去负数的计算,转化为该正数加上负数的补码。
4.补码的深入
一个负数的补码 = 模运算系统的模减去它的绝对值
照这么说,一个4位的运算器可以表示 2^4 = 16 个数,把它视为一个模16运算系统的话——
4位运算器负数的补码 = 16 - 该负数的绝对值
那么设有一个负数 -x,上式就转化为:
-x[补] = 2^4 - x
举个例子,-1的补码就是16-1 = 15,15表示为机器数就是1111。-2的补码是14,表示为机器数就是1110。-8的补码是8,表示为机器数就是1000。
按照这种编码方式,把4位模运算系统表示的16个机器数,也画成一个时钟的样子,如下。
16个数划分为了两半,一半表示正数(0算在内),一半表示负数。
顶头0000代表0,底下1000代表-8。0001到0111表示了0到7。1000到1111表示了-8到-1。
可以看到,0的表示是唯一的。
另外,假如有一减法,4 - 4。
4 - 4 = 4 + 4[补] = 4 + (2^4 - 4) = 4 + 12 = 0100 + 1100 (机器数) = 0000
0100 与 1100 相加的结果是 1 0000,因为是4位运算器,最高一位会被舍去,保留下来的就是0000即0。
可见,补码的编码方式不仅解决了0表示不唯一的问题(多出来的一位还能多表示一个负数),还方便了硬件的设计(把减法转化为加法),解决了符号参与计算的问题。故现代计算机普遍采用补码的编码方式。
一样不难发现,划分成了正数一半(0算在内)、负数一半,最高一位其实仍然表示了符号。
1000之前都是正数,除去最高位的后三位表示了机器数的真值,从1000开始变成负数,这是和原码反码一样的特性。
综上,因此有两个结论。
1.假设有n位运算器,负数 -x 的补码为
-x[补] = 2^n - x
减法的计算,等于加上负数的补码。
2.机器数的最高一位指示了正负号,0为正,1为负,同时也就划分了正数一半(包括0),负数一半。
假设有n位运算器,它用补码表示的数据范围为
[-2^(n-1), +2^(n-1)-1]
来源:https://www.cnblogs.com/banmei-brandy/p/12299655.html