原码、反码、补码

本贴最后更新于 2372 天前,其中的信息可能已经时移世异

学过计算机原理的人都知道原码、反码、补码,但是有多少人知道为什么会有这三种码呢,这三种码又是用来干嘛的呢。
  众所周知,在计算机的世界只有 01,那么显然所有的数都得转成二进制,这样计算机才能够理解。如何将一个十进制的数转成二进制就不说了,说下原码,正数的原码就是十进制转成二进制得到的二进制值,而负数是对应的正数转成二进制得到的二进制值,然后将最高位(符号位)置为 1 表示这是一个负数,如-10:10001010。

1. 原码

  计算机进行算术运算时为了简单效率所以要求能够使用加法代替减法,如 1-1==1+(-1)==0,那么我们先看看原码能不能实现这种需求。
示例:

计算76-10==66
   十进制     二进制
   76        01001100
 +  -10       10001010
---------------------
   66        11010110(-86)
2. 反码

  从上面算出的结果可见原码是无法完成对减法的运算需求的,那么由于 1-1==1+(-1),所以人类又找到了一个看似能够解决这个问题的解决方法——反码,即将负数的符号位不变其余位取反。下面我们再看看反码能不能解决问题。
示例 1:计算 15-125

计算15-125==-110
   十进制     二进制原码    二进制反码
   15        00001111    00001111
 +  -125      11111101    10000010
---------------------------------
   -110      11101110    10010001
得到10010001(反码)==11101110(原码)==-110,正确。注意:使用反码计算得到的结果也是反码,需要再次转换成原码。

示例 2:计算 76-10

计算76-10==66
   十进制     二进制原码    二进制反码
   76        01001100    01001100
 + -10       10001010    11110101
---------------------------------
   66        01000010    101000001==01000010  
这里得到的值超过8bit,所以最高的1需要丢弃,丢弃后需要在最低位+1,得到01000010(反码)==01000010(原码)==66,正确。

示例 3:计算 1-1

计算1-1==0
   十进制     二进制原码    二进制反码
   1         00000001    00000001
 + -1        10000001    11111110
---------------------------------
   0         10000000    11111111  
得到11111111(反码)==10000000(原码)==-0,-0?通过反码计算会出现+0和-0,一个0对应了两个码,显然是不合理的。

  从上面三个例子可以看出使用反码进行减法运算时存在两个问题:
  1. 当计算结果溢出时需要额外进行 +1 操作,使得运算多了一步,效率降低
  2. 0 存在 +0 和-0 两种存在方式,不方便理解

3. 模与互补、同余

  在看补码之前,先介绍三个概念——模、补数、同余。我们从现实生活举例来看:

  • 我们将一个时钟的分针往前拨 20 分钟,和往后拨 40 分钟,得到的结果是一样的。
  • 把你的属年(属猴)往后退 5 年,和往前进 7 年,一样都是属兔。
  • 把数字 87,减去 25,和加上 75,在不考虑百位数的条件下,得到的结果都是 62。

  上述几组数字,有这样的关系:
    20 + 40 = 60
    5 + 7 = 12
    25 + 75 = 100
  式中的 60、12 和 100,就是“模”。
  式中的 20 和 40、5 和 7,以及 25 和 75,就是一对对“互补”的数字。
  而且 20,80,140 在模是 60 的情况下就是互为“同余”的数字。
  通俗解释下模、补数、同余的概念:

  • :就是一个轮回,比如分针转一圈,十二生肖一轮等等。
  • 互补 :一个数值针对某个模的互补值就是这个数值加上或者减去多少能够等于模,或者等于模的同余值。
  • 同余 :一个数值加上或者减去模的整数倍得到的所有数值即为该数值的同余值**(也就是除上模,余数是一样,所以叫同余)**,0 是模的同余,-模也是模的同余。

  理解了什么是模,什么是互补、什么是同余,那么如果给一个模,以及一个值 a,如果计算 a 的补数(与 a 互补的值)呢,其实很简单,只需要拿模-a 即可,计算同余值可以直接加上或者减去模的整数倍即可。

4. 那么互补的值有什么用呢?

  如果我们在进行减法运算时,用与减数互补的值代替减数与被减数进行加法运算会发生什么呢?废话不多说,看示例。
示例 1:在分钟刻度下,计算 55 分钟往后拨动 34 分钟,转化成数学计算就是:55-34

被减数      55
减数        34
减数补数   60-34==26
最终结果     55+26==81
---------------------
用减数补数代替减数得到结果为81,81在分钟刻度盘上正好是21,也就是81是21的同余值,和55-34是一样的。注意:这里涉及到类似上面的87+75的情况,即忽略了进位。

示例 2:在十二生肖中,计算猴年往后退 11 年,转化成数学计算就是:9-11

被减数      9
减数        11
减数补数   12-11==1
最终结果     9+1==10
-------------------
用减数互补值代替减数得到结果为10,10对应到十二生肖正好是鸡,和猴年往后退11年是一样的,所以得到的也是一个同余值。

  从上面的示例可以看出,使用互补值计算出的结果与实际值其实是同余的关系。

5. 二进制的模

  上面看了分钟刻度盘的模,十二生肖的模,以及两位整数的模,那么对于一个 8bit 的字节的模是多少呢?
  分钟刻度盘的模为什么是 60?是因为他的值是从 1-59,总共 60 个值,十二生肖以及两位整数也是一样的,所以我们只需要看看一个 8bit 的字节的所有取值一共是多少个就是他的模,显示 8 个 bit 可表示的最小值是 00000000==0,最大值是 11111111==255,那么从 0 到 255 一共是 256 个值,所以一个 8bit 的字节的模就是 256 了。但是其实在计算机中为了能够表示负数,所以讲 8bit 的字节的最高位设为符号位,0 表示整数,1 表示负数,所以能够表示数值的也就只有 7bit,如果我们忽视符号位,那么剩下 7bit 的模就是 128,而不是 256 了。**下面在计算时我们会直接使用 128 而非 256!**

6. 使用互补值进行二进制的减法计算

  下面我们就来看看如果使用互补值来进行二进制的减法计算,我们先来看一个公式:假设模式 M,我们计算 X-Y,然后我们使用减数的补数来计算,看看下面的换算:

X-Y == X+(M-Y) == X+((M-1)-Y+1)

  下面我们来看示例,这个公式在下面会用到的。

示例 1:计算 15-125

           十进制          二进制
被减数      15             0001111
减数        125            1111101
减数补数   128-125==3     0000011
最终结果     18             0010010
------------------------------------
得到0010010==18,在模式128的情况下,18正好是-110的同余值,跟上面现实的例子是一样的!

示例 2:计算 76-10

           十进制          二进制
被减数      76             01001100
减数        10            00001010
减数补数   256-10==246   11110110
最终结果     322    101000010
------------------------------------
得到101000010==322,在模式256的情况下,322正好是66的同余值,结果还是一样!

  从上面两个例子我们应该可以看出,如果我们使用减数的补数进行加法运算,那么得到的结果就是一个与正确结果同余的值。在现实生活中,我们可以直接把两个同余的值看做是相同的,例如分钟 20 和分钟 80 完全就是一样的,那么在计算机里我们可以这么假设吗?答案是可以的,看下面。
  试想当计算机使用一个 7bit 的空间保存一个数值时是如何保存的,比如 18,我们可以这么推算,首先分配一个 7bit 的空间,每个 bit 上的值都是 0,那么如何表示 18 呢?我们可以这样理解:往这个 7bit 的空间内进行 18 次加 1 操作,满 2 就进 1,最终就会得到 0010010。那么如何表示-110,我们可以理解为往这个 7bit 的空间内进行 110 次减 1 操作,一开始全是 0,那么如何减 1 呢?很简单直接减成 1111111 即可,可以这样理解,分钟在 0 刻度,你往后拨一下就会指向 59,这里也是这个道理,所以连续减 110 次,就会得到 0010010,根 18 是一样的,所以在计算机看来 18 和-110 是一样的。
也就是说 15-125 == 15+(128-125) == 15+(127-125+1) (上面的公式),也就是说-125 被 127-125+1 代替了,那么**127-125+1(M-)**又是什么?

7. 补码

  上面一路走来终于证明了使用补数可以代替减法,下面我们要解决的问题是 M-1-Y+1 是啥。
我们直接看二进制如何计算 M-1-Y+1。
示例:计算 M=128,Y=110

     十进制   二进制
 M-1  127   1111111
 -Y   110   1101110
            0010001
--------------------
M-1换算成二进制就是N位1,那么N位1减去任何一个N位的二进制是啥呢?其实就是按位取反!因为遇到0,1-0==1,取反,遇到1,1-1==0,取反,所以整体就是按位取反,也就是反码。

 +1   1     0000001
            0010010
 ------------------
所以总体就是在110的二进制基础上按位取反然后加1,也就是110的反码加1。

  看了上面的示例,应该知道 M=128,Y=110,M-1-Y+1 就是 Y 的反码加 1,也就是说,如果我们需要计算 X-Y,只需要计算 X+(Y 的反码 +1),由于我们得出这个结论是使用**补数替代减法**得到的,所以**Y 的反码 +1**就被叫做 Y 的**补码**。
  到这里我们知道了 110 的补码,上面我们介绍了计算机使用 1 字节的最高位表示符号位,1 表示负数,所以-110 的最高位是 1,由于在使用补码进行减法运算过程中最高位并不参与运算,所以这个最高位应该是固定不动的,所以负数的反码补码最高位始终都是 1。**也就得到了-110 的补码是:10010010**。对于正数,符号位是 0,那么反码补码最高位就始终是 0,而且对于正数在计算时也无需使用其补码进行操作,但是为了统一都是用补码,所以定正数的反码补码都等于原码。
  根据补码的计算过程有些文章会说一个负数 X 的补码对应的值==2^n-|X|,理解了上面的过程这个公式就自然懂了,不过这个公式没啥用,也没必要记。
  到这里终于把**补码**的来历说清楚了,至少我自己是明白了,但愿读者也可以明白吧!

一些补码的其他知识

  上面我们看了 7bit 的模式 128,也就说是能表示 0-127 共 128 个数值,加上最高位的符号位就成了-127-127 共计 255 个数值,因为没有-0 这个数字。但是实际对于计算机来说 8bit 的空间是可以表示 256 个数字的,那么还有一个数字是啥呢?正是:10000000(注意:这是补码,因为计算机都存的补码)。我们可以试着计算下 10000000 的原码,可以得到 10000000 的原码就是 10000000,也就是-0,但是如果存在 +0 和-0 两个计算机码对应一个值(+0 和-0 都是 0),那么显然是没必要的,而且还会造成混乱,所以人为的规定 10000000 表示-128。所以一个 8bit 的空间可以表示的数字就是从-128 到 127 了,而不是-127-127!

https://blog.csdn.net/vickyway/article/details/48788769

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...