【计算机组成原理】原码、反码、补码

前言

因为CPU运算器中只有加法器,所有要把减法转换加法来运算,同时也是为了节约成本。
我们知道,根据运算法则减去一个正数等于加上一个负数,即:1-1 = 1 + (-1) = 0 ,所以机器可以只有加法而没有减法,这样计算机运算的设计就更简单了。

于是人们想出了将符号位也参与运算的方法。
对于有符号数,符号的“正”“负”机器无法识别,因为正负刚好是截然两种状态,用”0“表示”正“,用”1“表示”负“,这样符号就被数字化了,规定将它们放在有效数字前面,组成有符号数。

原码

人们开始探索 将符号位参与运算,并且只保留加法的方法。首先来看原码:

一个正数,按照绝对值大小转换成的二进制数;一个负数按照绝对值大小转换成的二进制数,然后最高位补1,称为原码
比如
00000000 00000000 00000000 00000101 是 5的 原码。
10000000 00000000 00000000 00000101 是-5的 原码。


计算十进制的表达式:1-1=0
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [1000 0010]原 = -2

  1 - 1 
= 1 + (-1) 
= [0000 0001]+ [1000 0001]= [1000 0010]= -2

如果用原码表示,让符号位也参与计算,显然对于减法来说,结果是不正确的。这也就是为何计算机内部不使用原码表示一个数。

备注:

比如byte类型,用2^8来表示无符号整数的话,是0 - 255了;
如果有符号, 最高位表示符号,0为正,1为负,那么,正常的理解就是 -127 至 +127 了,这就是原码了。

值得一提的是,原码的弱点,有2个0,即+0-00000 00001000 0000);
还有就是,进行异号相加或同号相减时,比较麻烦,先要判断2个数的绝对值大小,然后进行加减操作,最后运算结果的符号还要与大的符号相同。

于是,反码产生了。

反码

为了解决原码做减法的问题,出现了反码:

  • 正数的反码与原码相同。
  • 负数的反码为对该数的原码 除符号位外各位取反[每一位取反(除符号位)]。

取反操作指:原为1,得0;原为0,得1。(1变0; 0变1)
比如:

  • 正数 5 00000000 00000000 00000000 00000101 的反码
    还是— 00000000 00000000 00000000 00000101
  • 负数 -510000000 00000000 00000000 00000101 的反码
    则是— 11111111 11111111 11111111 11111010
    反码是相互的,所以也可称:10000000 00000000 00000000 0000010111111111 11111111 11111111 11111010互为反码。

计算十进制的表达式: 1-1=0

  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;

而使用补码表示时又可以多保存一个最小值。.
比如:
-5的原码是10000000 00000000 00000000 00000101
反码是:11111111 11111111 11111111 11111010

那么,补码为:
11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011


计算:

  1 - 1 
= 1 + (-1) 
= [0000 0001]+ [1000 0001]= [0000 0001]+ [1111 1111]= [0000 0000]= [0000 0000]

这样0用[0000 0000]表示, 而以前出现问题的-0 则不存在了,而且可以用[1000 0000]表示-128

  (-1) + (-127) 
= [1000 0001]+ [1111 1111]= [1111 1111]+ [1000 0001]= [1000 0000]

-1 - 127的结果应该是-128,在用补码运算的结果中,[1000 0000]补 就是-128
但是注意,因为实际上是使用以前的-0的补码来表示-128,所以 -128并没有原码和反码表示。(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原,这是不正确的)

使用补码,不仅仅修复了0的符号以及存在两个编码的问题,而且还能够多表示一个最低数。这就是为什么8位二进制,使用原码或反码表示的范围为[-127, +127],而使用补码表示的范围为[-128, 127]

因为机器使用补码,所以对于编程中常用到的32位int类型,可以表示范围是: [-2^31, 2^31-1] 因为第一位表示的是符号位。

从补码求原码的方法跟原码求补码是一样的取反再加1 ,也可以通过完全逆运算来做(除了-128也就是[1000 0000]补 ),先减一,再取反。

补码的补码=原码

针对负数而言,
任意8位二进制负数:

  • 原码+反码=1111 1111
  • 补码=反码+1
  • 补码+补码的反码=1111 1111
  • 补码的补码=补码的反码+1

所以 反码+1+补码的补码-1=1111 1111

所以 反码+补码的补码=1111 1111

所以 补码的补码=1111 1111-反码=原码

总结

  • 三种机械数的最高位均为符号位。
  • 符号位和数值位可以用.或者,隔开。
  • 当真值为正时,原码、补码、反码的表现形式均相同,符号位为0,数值部分和真值相同。
  • 当真值为负时,原码、补码、反码的表现形式均相同,符号位为1,数值部分,补码是原码的”求反加1’,反码是原码的“每位求反”。

猜你喜欢

转载自blog.csdn.net/weixin_44394801/article/details/123076749