首先,确定一点,计算机中的储存信息的方式是以二进制的形式进行存储的。
而原码指的是对数字二进值的定点表示方法,之所以采取二进制来保存数据的原因是二极管只能表示两种状态,通或者不通,即0和1。
在进行数字运算的时候,只要将10进制的数据转为2进制,就可以快捷的完成运算。只不过这种运算方式在涉及到负数运算时就会出现问题,举个例子。
10进制运算:
-1 + 2 = 1;
二进制运算
1001
+
0010
|
1011
1011在转化为十进制为-3(最高位是1时表示负数)
这样的结果明显不符合我们的认知,那么错在什么地方呢?
原因在于我们使用二进制进行有符号运算时,会将最高位设置为符号位,来区分正数和负数,即将一个数据分为了符号部分和数值部分,举个例子
1011(-3)
最高位的1是符号位,后三位是表示数值的数值位,分别设为a和b,合起来为a*b;
我们在进行加减运算时,符号位是不参加运算的,只进行数值位的运算,比如
1011
+
0001
=
1100
-3+1=4,这明显是错误的。原因就在于没有考虑到符号的影响,我们所有的计算都
在数值位上进行:
```cpp
设符号位是a,设数值位的值为b,十进制的值就是ab
现在加上一个数,a*(b+2)=ab+2a;当a是1时,表达式的值为b+2,当a是-1时,表达式
的值为-b-2,a的值影响到了整体的计算结果。
从上面的计算已经知道,利用原码进行计算,会在正数和负数同时出现的运算上出现问题,本质的原因在于一个正数和一个负数进行运算时,符号位永远是1,导致原本做的加法变成了减法。在数学关系上,我们都知道负负得正,如果引入另外一个值,使得通过这个值既能反推出原码的值,又能用来做正确的数值计算,那么二进制运算的问题将得到完美解决。于是,反码应运而生。
要想做到负负得正,想要抵消符号位带来的影响,那么反码的数值变化趋势就要与
原码相反,在数学上表现为a+b=C,a代表原码的数值,b代表反码的数值,b如果变大
,a就会变小,两者相加得到常数。
这个常数要能够针对不同的原码,变化出对应的反码。这里我们根据现在得到反码的规则去反推这个常数,反码是原码的符号位不变,其余位全部取反而得到的。
比如
1001的反码为1110,可以很容易的看出在符号位不参加运算时,原码和反码的数值位
相加得到的结果就是数值位全部为1的值
反码在进行负数的相关运算时,如果计算的结果没有超过0,是没有问题的,但是如果过超过0,就会出错,永远比正确结果小1
考虑下面的计算:
-1+2=1(10进制形式)
将-1和2转化为二进制的反码进行运算
1001-->1110(反码)
0010-->0010(反码)
结果为->0000,十进制下为0,如果考虑最高位的1,应该是-0,问题也就出在这个-0
上
请看下面这张对照表‘
原码-------反码 十进制数值
0000-------0000 +0
1111-------1000 -0
1001-------1000 -1
1010-------1101 -2
1011-------1100 -3
1100-------1011 -4
1101-------1010 -5
1110-------1001 -6
如果让-6加上3,你可以得到正确的-3,但是如果你让-6+7,你会得到0,本质在于,在反码中,0的表示方法有两种,换句话说,0数了两下。
你可能在C语言的教科书或者单片机的教科书中,看到过这样一段话
一个8位的储存单元能够表示数值的大小范围是-128~127,你可能觉得很奇怪
,127还比较好理解,由于符号位的原因,真正用于表示数值的位只有7位,加起来就是2^7-1,但是-128是怎么算出来的呢?不应该是-127吗?造成这样困扰的罪魁祸首便是补码,在这张表中
原码-------反码 十进制数值
0000-------0000 +0
1111-------1000 -0
1001-------1000 -1
1010-------1101 -2
1011-------1100 -3
1100-------1011 -4
1101-------1010 -5
1110-------1001 -6
我们注意到0出现了两次,只要让其中一个0失效,就可以解决所有问题了,不管是负
数的运算,还是两个0问题,都解决了。解决方法也很简单,利用之前的思想,再引入
一种新的编码方式,这种方式在反码的基础上规定只有所有位都是0才是0,其余数值
全部在反码的基础上加一,这样,原来构成一种新的码,即补码,新的对照表如下
原码-------反码----- 补码 十进制数值
0000-------0000------0000 +0
1000-------1111----- 0000 -0
1001-------1110----- 1111 -1
1010-------1101----- 1110 -2
1011-------1100----- 1101 -3
1100-------1011----- 1100 -4
1101-------1010----- 1011 -5
1110-------1001----- 1010 -6
由于补码是在反码的基础上加1得到的,-128没有对应的原码和反码