计算机中存在的编码方式
首先理解这三句话:
1、从计算机角度看,所谓的原码、反码、补码与移码,都只是1与0的组合排列。
我们在理解这几种编码的时候,要尝试从计算机角度思考。只要计算机层面(也就是电路的设计层面)逻辑是对的,我们可以赋予它任何含义(编译器就是依照我们统一的含义设计的)。
2、我们进行编码的目的有两个,一个是能让数字转化为机器语言,还有一个就是编码能够在机器层面正确且高效进行运算。
3、所有的设计都是出自对问题的解决或优化。
真值为-2(4位为例分析,真值为-0010)
原码:1010
反码:1101
补码:1110
真值为-3(4位为例分析,真值为-0011)
原码:1011
反码:1100
补码:1101
下面给出定义
真值
定义:真实所表示的数值。
原码
定义:在数值前增加了一位符号位(即最高位),正数该位为0,负数该位为1(0有+0与-0两种表示),其余位表示数值的大小。
其达到了了第一个目的,但是没有达到第二个目的。因为用原码进行加减运算在电路设计层面是非常麻烦的,且低效。
例:
十进制:-2 - 3 = -5
二进制:1010 - 0011 = 0111 结果为+7,结果不符合编码规则
我们完全可以设计一个电路,来使得结果为1101。如果有兴趣,可以去尝试一下,但是明显,这样设计出来的电路是不符合我们的运算逻辑的,于是反码被发掘。
反码
定义:
负数:对原码除符号位外的其余各位逐位取反。
正数:原码本身
反码的诞生是为了解决原码的问题,反码能够在电路设计层面较简单的实现加减法运算,但是存在+0与-0的区别(人的观念里是没有+0与-0的概念的),且加与减的电路设计是不一样的。电路的设计依旧比较麻烦。
例1:
十进制:-2 - 3 = -5
二进制:1101 - 0011 = 1010 结果正确,符合编码规则
例2:
十进制:-2 + (-3) = -5
二进制:1101 + 1100 = 1001(-6)结果不符,不符合编码规则
例3:
十进制:-7 + 7 = 0
二进制:1000 + 0111 = 1111 (-0),出现负0,但我们是不希望有-0与+0的区分的
就这三个例子可以发现两个问题:负数+负数结果是错误的。-N+N结果为-0,我们是不希望0有+0与-0之别的。(有兴趣可以按照下述补码分析方式分析一下反码在哪些运算情况是符合规则与逻辑的)
补码
补码的出现解决了反码的两个问题
1)、能够让0用唯一编码表示,还多表示了一个数;
2)、所有的加减法运算都能统一到加法运算。电路设计相对简单,且高效。
下面证明:
先给出定义:给定一个n位二进制数N(负数的绝对值原码表示)。
1、负数-N的二进制补码的定义:当N不等于0的时候,N的补码为2^n-N; 当N等于0时,其补码为2^n-0,舍掉高位。
2、正数N的补码为其自身。
注意,这里的正负数的补码定义,在计算机看来是没有的,是我们人为赋予的,我们只是把1与0的线性表示映射到了我们能看懂的文字,两者的转换时编译器的工作。
1、统一+0与-0:
N = 0 时,-N补码为2^n -0 = 2^n 即为 1 0000 0000,最高位舍去。+N补码为0000 0000,两者得到统一。
N = 2^(n-1)时, -N的补码为2^n - 2^(n-1) = 2^(n-1) 即1000 0000
2、能够把加减法统一成加法。
以下分析不考虑溢出的情况,溢出也是按此方式进行,但是结果不是我们所想的结果:
为了方便理解,我把一个补码表示的负数分为数值部分(低n-1位)和符号位(最高位)部分:
1、负数+负数
数值部分:(因为符号位大小为2^(n-1), 所以数值部分为2^(n-1) - N)
2^(n-1) - M + 2^(n-1) - N = 2^n - (M+N) = 2^(n-1) + 2^(n-1) - (M+N),正常情况下,M+N应该小于等于2^(n-1); 相当于完成了一次进位(多出来的一个2^(n-1) )。
数值部分为负数的补码表示
符号位部分:
1+1+1(进位)=1(这也符号位与用1表示负数,用0表示正数契合),因此运算结果还是负数。
举例:
十进制:-2 +(-3) = -5
补码:1110 + 1101 = 1011
数值位:110+101 = 2³-2 + 2³ - 3 = 2³ + 2³ - 5
符号位:1+1+1(进位的1)=1 高位舍去
2、正数+正数
数值部分:
M + N 的原码表示 (正数没有补码,或者说正数的补码就是自身),正常情况下,M+N应该小于等于2^(n-1)-1。
符号位部分:
0+0=0
举例:
十进制:2 + 3 = 5
补码:0010 + 0011 = 0101
数值位:010+011=101
符号位:0+0=0(没有进位,所以只有两个数的符号位0+0)
3、正数+负数
数值部分:
M + 2^(n-1) - N = 2^(n-1) -(N-M) = 2^(n-1) + M - N
如果N>M,则结果为负数的补码表示
如果N≤M,则产生进位(多出来的一个2^(n-1) ),数值部分M-N的值的二进制原码表示
符号位部分:
如果N>M:0+1=1;符号位是1,表示负数
如果N≤M:0+1+1=0(产生的进位);符号位为0,表示正数
举例1:
十进制:2 +(-3) = -1
补码:0010 + 1101 = 1111
数值位:010+101 = 2 + 2³ - 3 = 2³ - 1(即为111)
符号位:0+1=1
举例2:
十进制:3 +(-2) = 1
补码:0011 + 1110 = 0001
数值位:011+110 = 3 + 2³ - 2 = 2³ + 1(2³进位,剩一个1即0001)
符号位:0+1+1=0(低位有进位,向高位进位舍去)
4、负数+正数
数值部分:
2^(n-1) - M + N = 2^(n-1) - (M-N) = 2^(n-1) + N - M
如果M>N,则产生进位,数值部分为负数的补码表示。
如果M≤N,产生进位,且结果就是N-M的原码(正数的补码就是原码)表示
符号位部分:
如果M>N:0+1=0;符号位是1,表示负数
如果M≤N:0+1+1(产生了一次进位)=0;符号位为0,表示正数
类似上一例子
例子:
所有的减法都可以转化为加法运算
正数-正数 ——》正数+负数
正数-负数 ——》正数+正数
负数-负数 ——》负数+正数
负数-正数 ——》负数+负数
值得注意的是:在符号二进制补码运算中,二进制的加减运算与无符号数的加减运算遵循相同的运算规则,因而计算机只需要一套共用的硬件电路来处理这两种类型的算术运算。
在计算机看来,我们对两个数进行加减,进行的都是相同的操作。且该操作得到的1与0的排列结果符合我们编码规则,也就证明了这套编码规则是可实行的。