beautylan吧 关注:95贴子:3,251
  • 10回复贴,共1

【总结】反码,补码,移码的作用

只看楼主收藏回复

以前只知道 反码,补码,移码与原码之间如何相互转换
但是没有特意研究为什么要有反码补码和移码
今天查了些资料


IP属地:上海1楼2017-03-19 11:23回复
    1.
    http://blog.csdn.net/xdd19910505/article/details/40424931
    其实从原码到补码是一个层层递进的关系,也是一个在错误中逐步发展的过程。也就是说利用原码运算减法时出现的错误,为了解决出现了反码运算,但是反码运算时又出现了让人不满意的地方,于是为了更好的追求出现了补码。

    初中还是高中或者是小学让我们知道了这样一件事情:
    把某物体左旋转 90 度,和右旋转 270度,在不考虑圈数的条件下,最终的位置是相同的;
    270+90=360;
    把分针倒拨 20 分钟,和正拨 40分钟,在不考虑时针的条件下,效果也是相同的;
    20+40=60;
    把数字 87,减去 25,和加上75,在不考虑百位数的条件下,效果也是相同的;
    25+75=100;
    ……。
    总之都是会有一定的前提不考虑,可见它们的关系就是互补。而计算得到的360、60、100就是分别在不同情况下的模。知道模,就可以求一个数的补数。
    用补数代替原数,就可以用加法代替减法,出现一个进位就是一个模的值。
    二进制的模:
    有多少位参加运算,模就是1的后面加上多少个0。
    例如:2位二进制数参加运算,模就是100;即2^2=4
    8位二进制数参加运算,模就是100000000;即2^8=256
    二进制的补码:
    求二进制数的补数,目的是往计算机里面存放。一般情况下,都是以
    8 位二进制数来讨论补码。
    计算时加上正数,是不需要进行求取补码的;只有进行减法(或者加上负数),才需要对减数求补码。补码就是按照
    这个要求来定义的:正数不变,负数即用模减去绝对值。
    例如:X = -126,其补码为 1000 0010,计算方法如下:
    1 0000 0000
    — 0111 1110
    —————————————
    1000 0010
    其实就是为了存储数据,为了计算简单。既然补码就可以做到这些了,那为什么书上还有把原码和反码写出来呢蛊惑人心呢?个人认为它们是没有太多用处的,在计算机里是不存在的,就是为了可以形象的说明二进制是怎么求出补码的,只起到一个中间计算过程的作用,要不一下子写出一个数的补码也是要转几个弯的。


    IP属地:上海2楼2017-03-19 11:28
    收起回复
      http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
      摘抄
      三. 为何要使用原码, 反码和补码
      在开始深入学习前, 我的学习建议是先"死记硬背"上面的原码, 反码和补码的表示方式以及计算方法.
      现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
      [+1] = [00000001]原 = [00000001]反 = [00000001]补
      所以不需要过多解释. 但是对于负数:
      [-1] = [10000001]原 = [11111110]反 = [11111111]补
      可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?
      首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
      于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
      计算十进制的表达式: 1-1=0
      1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
      如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.
      为了解决原码做减法的问题, 出现了反码:
      计算十进制的表达式: 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-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类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.
      四 原码, 反码, 补码 再深入
      计算机巧妙地把符号位参与运算, 并且将减法变成了加法, 背后蕴含了怎样的数学原理呢?
      将钟表想象成是一个1位的12进制数. 如果当前时间是6点, 我希望将时间设置成4点, 需要怎么做呢?我们可以:
      1. 往回拨2个小时: 6 - 2 = 4
      2. 往前拨10个小时: (6 + 10) mod 12 = 4
      3. 往前拨10+12=22个小时: (6+22) mod 12 =4
      2,3方法中的mod是指取模操作, 16 mod 12 =4 即用16除以12后的余数是4.
      所以钟表往回拨(减法)的结果可以用往前拨(加法)替代!
      现在的焦点就落在了如何用一个正数, 来替代一个负数. 上面的例子我们能感觉出来一些端倪, 发现一些规律. 但是数学是严谨的. 不能靠感觉.
      首先介绍一个数学中相关的概念: 同余
      同余的概念
      两个整数a,b,若它们除以整数m所得的余数相等,则称a,b对于模m同余
      记作 a ≡ b (mod m)
      读作 a 与 b 关于模 m 同余。
      举例说明:
      4 mod 12 = 4
      16 mod 12 = 4
      28 mod 12 = 4
      所以4, 16, 28关于模 12 同余.
      负数取模
      正数进行mod运算是很简单的. 但是负数呢?
      下面是关于mod运算的数学定义:

      上面是截图, "取下界"符号找不到如何输入(word中粘贴过来后乱码). 下面是使用"L"和"J"替换上图的"取下界"符号:
      x mod y = x - y L x / y J
      上面公式的意思是:
      x mod y等于 x 减去 y 乘上 x与y的商的下界.
      以 -3 mod 2 举例:
      -3 mod 2
      = -3 - 2xL -3/2 J
      = -3 - 2xL-1.5J
      = -3 - 2x(-2)
      = -3 + 4 = 1
      所以:
      (-2) mod 12 = 12-2=10
      (-4) mod 12 = 12-4 = 8
      (-5) mod 12 = 12 - 5 = 7
      开始证明
      再回到时钟的问题上:
      回拨2小时 = 前拨10小时
      回拨4小时 = 前拨8小时
      回拨5小时= 前拨7小时
      注意, 这里发现的规律!
      结合上面学到的同余的概念.实际上:
      (-2) mod 12 = 10
      10 mod 12 = 10
      -2与10是同余的.
      (-4) mod 12 = 8
      8 mod 12 = 8
      -4与8是同余的.
      距离成功越来越近了. 要实现用正数替代负数, 只需要运用同余数的两个定理:
      反身性:
      a ≡ a (mod m)
      这个定理是很显而易见的.
      线性运算定理:
      如果a ≡ b (mod m),c ≡ d (mod m) 那么:
      (1)a ± c ≡ b ± d (mod m)
      (2)a * c ≡ b * d (mod m)
      如果想看这个定理的证明, 请看:http://baike.baidu.com/view/79282.htm
      所以:
      7 ≡ 7 (mod 12)
      (-2) ≡ 10 (mod 12)
      7 -2 ≡ 7 + 10 (mod 12)
      现在我们为一个负数, 找到了它的正数同余数. 但是并不是7-2 = 7+10, 而是 7 -2 ≡ 7 + 10 (mod 12) , 即计算结果的余数相等.
      接下来回到二进制的问题上, 看一下: 2-1=1的问题.
      2-1=2+(-1) = [0000 0010]原 + [1000 0001]原= [0000 0010]反 + [1111 1110]反
      先到这一步, -1的反码表示是1111 1110. 如果这里将[1111 1110]认为是原码, 则[1111 1110]原 = -126, 这里将符号位除去, 即认为是126.
      发现有如下规律:
      (-1) mod 127 = 126
      126 mod 127 = 126
      即:
      (-1) ≡ 126 (mod 127)
      2-1 ≡ 2+126 (mod 127)
      2-1 与 2+126的余数结果是相同的! 而这个余数, 正式我们的期望的计算结果: 2-1=1
      所以说一个数的反码, 实际上是这个数对于一个膜的同余数. 而这个膜并不是我们的二进制, 而是所能表示的最大值! 这就和钟表一样, 转了一圈后总能找到在可表示范围内的一个正确的数值!
      而2+126很显然相当于钟表转过了一轮, 而因为符号位是参与计算的, 正好和溢出的最高位形成正确的运算结果.
      既然反码可以将减法变成加法, 那么现在计算机使用的补码呢? 为什么在反码的基础上加1, 还能得到正确的结果?
      2-1=2+(-1) = [0000 0010]原 + [1000 0001]原 = [0000 0010]补 + [1111 1111]补
      如果把[1111 1111]当成原码, 去除符号位, 则:
      [0111 1111]原 = 127
      其实, 在反码的基础上+1, 只是相当于增加了膜的值:
      (-1) mod 128 = 127
      127 mod 128 = 127
      2-1 ≡ 2+127 (mod 128)
      此时, 表盘相当于每128个刻度转一轮. 所以用补码表示的运算结果最小值和最大值应该是[-128, 128].
      但是由于0的特殊情况, 没有办法表示128, 所以补码的取值范围是[-128, 127]


      IP属地:上海3楼2017-03-19 11:30
      回复
        以上主要说了补码,反码的作用,
        下面说移码的作用:
        作者:知乎用户
        链接:https://www.zhihu.com/question/24115452/answer/95805511
        来源:知乎
        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
        移码,故名思意,就是在原码的基础上加上一个偏移量。
        什么?!它为啥要这样?
        至于为什么要用移码表示exponent,这当然是有它的原因的啦~
        想一下,在进行浮点数的加减运算时,我们需要怎么办?
        忘了?
        好吧。。。想一想,我们对两个用科学记数法表示的数进行加减法的时候,我们怎么做最简单?
        通过比较exponent的大小,然后通过移动小数点,让它们一致,之后,把数值部分相加,即可。
        同样的,在计算机硬件的实现上,也是这样处理浮点数的加减法的~也就是通常所说的:求阶差、对阶,尾数相加,结果规格化(这个不知道是啥意思不要紧,只要知道是把得到的结果调整成为官方格式就好)
        那么,这就产生了一个问题:如何比较两个阶的大小,以右移小阶所对应的fraction呢?
        在原码的情况下,这样的比较是不方便的!
        因为,我们规定,对于负数,符号位是1;正数,符号位是0。
        那么一个正数01xxx和另一个正数00xxx比较,显然,01xxx大。
        但是,一个正数0xxx和一个一个负数1xxx比较。。。还是按照上面的比较的话,我们认为是1xxx那个大。。。
        为了一个比较设计不同的电路?不划算,不如,加个偏移量,让负数都变成正数吧。
        对!这样一来,比较就变得容易了!一套简单电路即可实现~~~


        IP属地:上海4楼2017-03-19 11:43
        收起回复
          作者:知乎用户
          链接:https://www.zhihu.com/question/24115452/answer/136760699
          来源:知乎
          著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
          我来解释下为什么IEEE标准中,8位阶码的偏置为127。
          1、8位移码的取值范围为0~255(00000000~11111111),但在浮点数的阶码中,00000000与11111111被保留用作特殊情况,所以阶码可用范围只有1~254,总共有254个值。
          2、8位有符号数取值范围为-128~+127(10000000~01111111),这里的二进制用补码表示,其中特别规定补码10000000没有原码,为-128的补码,总共有256个值。
          3、如果采用偏置128,在表达+127时会产生上溢(移码11111111被保留),所以在阶码中偏置为(128-1),与此同时,在表达-127时会产生下溢(移码00000000被保留),所以阶码中去掉-127与-128,取值范围为-126~127,总共254个值。
          ——————————————————————————————————————————
          另外我再补充一下对32位float型数据的取值范围的研究:
          <img src="https://pic4.zhimg.com/v2-f662e8fb8c8e0785ff608eabafbbc9b7_b.jpg" data-rawwidth="693" data-rawheight="93" class="origin_image zh-lightbox-thumb" width="693" data-original="https://pic4.zhimg.com/v2-f662e8fb8c8e0785ff608eabafbbc9b7_r.jpg">最高位为符号位;
          最高位为符号位;
          指数:共8个bit,占据30~23位;
          底数:实际是占用24个bit,由于其最高位始终为1,所以最高位省去不存储,在存储中只有23个bit,占据22~0位;
          当22~0位全部置1时,底数取得最大值,接近于2。
          <img src="https://pic1.zhimg.com/v2-3a42dc5d9f3aa63cb9f34bf21245b48c_b.jpg" data-rawwidth="675" data-rawheight="269" class="origin_image zh-lightbox-thumb" width="675" data-original="https://pic1.zhimg.com/v2-3a42dc5d9f3aa63cb9f34bf21245b48c_r.jpg">
          当22~0位全部置0时,底数取得最小值,为1。
          <img src="https://pic4.zhimg.com/v2-1d9b1979deb0057ab2a9b87d04a9b8a3_b.jpg" data-rawwidth="656" data-rawheight="264" class="origin_image zh-lightbox-thumb" width="656" data-original="https://pic4.zhimg.com/v2-1d9b1979deb0057ab2a9b87d04a9b8a3_r.jpg">因此float型的取值范围为:因此float型的取值范围为:
          -2*2^127 ~ -1*2^(-126) 与 1*2^(-126) ~ 2*2^127
          转化得:
          -3.4*10^38 ~ -1.2*10^(-38) 与 1.2*10^(-38) ~ 3.4*10^38


          IP属地:上海5楼2017-03-19 11:43
          回复
            海明码校验:
            http://blog.csdn.net/lycb_gz/article/details/8214961
            以下内容摘自笔者最新出版的著作《深入理解计算机网络》一书:http://item.jd.com/11165825.html
            本书原始目录参见此文:http://blog.csdn.NET/lycb_gz/article/details/8199839
            5.3.6 海明纠错码
            海明码(Hamming Code)是一个可以有多个校验位,具有检测并纠正一位错误代码的纠错码,所以它也仅用于信道特性比较好的环境中,如以太局域网中,因为如果信道特性不好的情况下,出现的错误通常不是一位。
            海明码的检错、纠错基本思想是将有效信息按某种规律分成若干组,每组安排一个校验位进行奇偶性测试,然后产生多位检测信息,并从中得出具体的出错位置,最后通过对错误位取反(也是原来是1就变成0,原来是0就变成1)来将其纠正。
            要采用海明码纠错,需要按以下步骤来进行:计算校验位数→确定校验码位置→确定校验码→实现校验和纠错。下面来具体介绍这几个步骤。本文先介绍除最后一个步骤的其它几个步骤。
            1. 计算校验位数
            要使用海明码纠错,首先就要确定发送的数据所需要要的校验码(也就是“海明码”)位数(也称“校验码长度”)。它是这样的规定的:假设用N表示添加了校验码位后整个信息的二进制位数,用K代表其中有效信息位数,r表示添加的校验码位,它们之间的关系应满足:N=K+r≤2r-1。
            如K=5,则要求2r-r≥5+1=6,根据计算可以得知r的最小值为4,也就是要校验5位信息码,则要插入4位校验码。如果信息码是8位,则要求2r-r≥8+1=9,根据计算可以得知r的最小值也为4。根据经验总结,得出信息码和校验码位数之间的关系如表5-1所示。
            表5-1 信息码位数与校验码位数之间的关系
            信息码位数 1 2~4 5~11 12~26 27~57 58~120 121~247
            校验码位数 2 3 4 5 6 7 8
            2.确定校验码位置
            上一步我们确定了对应信息中要插入的校验码位数,但这还不够,因为这些校验码不是直接附加在信息码的前面、后面或中间的,而是分开插入到不同的位置。但不用担心,校验码的位置很容易确定的,那就是校验码必须是在2n次方位置,如第1、2、4、8、16、32,……位(对应20、21、22、23、24、25,……,是从最左边的位数起的),这样一来就知道了信息码的分布位置,也就是非2n次方位置,如第3、5、6、7、9、10、11、12、13,……位(是从最左边的位数起的)。
            举一个例子,假设现有一个8位信息码,即b1、b2、b3、b4、b5、b6、b7、b8,由表5-1得知,它需要插入4位校验码,即p1、p2、p3、p4,也就是整个经过编码后的数据码(称之为“码字”)共有12位。根据以上介绍的校验码位置分布规则可以得出,这12位编码后的数据就是p1、p2、b1、p3、b2、b3、b4、p4、b5、b6、b7、b8。
            现假设原来的8位信息码为10011101,因现在还没有求出各位校验码值,现在这些校验码位都用“?”表示,最终的码字为:??1?001?1101。
            3. 确定校验码
            经过前面的两步,我们已经确定了所需的校验码位数和这些校验码的插入位置,但这还不够,还得确定各个校验码值。这些校验码的值不是随意的,每个校验位的值代表了代码字中部分数据位的奇偶性(最终要根据是采用奇校验,还是偶校验来确定),其所在位置决定了要校验的比特位序列。总的原则是:第i位校验码从当前位开始,每次连续校验i(这里是数值i,不是第i位,下同)位后再跳过i位,然后再连续校验i位,再跳过i位,以此类推。最后根据所采用的是奇校验,还是偶校验即可得出第i位校验码的值。
            1)计算方法
            校验码的具体计算方法如下:
            p1(第1个校验位,也是整个码字的第1位)的校验规则是:从当前位数起,校验1位,然后跳过1位,再校验1位,再跳过1位,……。这样就可得出p1校验码位可以校验的码字位包括:第1位(也就是p1本身)、第3位、第5位、第7位、第9位、第11位、第13位、第15位,……。然后根据所采用的是奇校验,还是偶校验,最终可以确定该校验位的值。
            p2(第2个校验位,也是整个码字的第2位)的校验规则是:从当前位数起,连续校验2位,然后跳过2位,再连续校验2位,再跳过2位,……。这样就可得出p2校验码位可以校验的码字位包括:第2位(也就是p2本身)、第3位,第6位、第7位,第10位、第11位,第14位、第15位,……。同样根据所采用的是奇校验,还是偶校验,最终可以确定该校验位的值。
            p3(第3个校验位,也是整个码字的第4位)的校验规则是:从当前位数起,连续校验4位,然后跳过4位,再连续校验4位,再跳过4位,……。这样就可得出p4校验码位可以校验的码字位包括:第4位(也就是p4本身)、第5位、第6位、第7位,第12位、第13位、第14位、第15位,第20位、第21位、第22位、第23位,……。同样根据所采用的是奇校验,还是偶校验,最终可以确定该校验位的值。
            p4(第4个校验位,也是整个码字的第8位)的校验规则是:从当前位数起,连续校验8位,然后跳过8位,再连续校验8位,再跳过8位,……。这样就可得出p4校验码位可以校验的码字位包括:第8位(也就是p4本身)、第9位、第10位、第11位、第12位、第13位、第14位、第15位,第24位、第25位、第26位、第27位、第28位、第29位、第30位、第31位,……。同样根据所采用的是奇校验,还是偶校验,最终可以确定该校验位的值。
            ……
            我们把以上这些校验码所校验的位分成对应的组,它们在接收端的校验结果(通过对各校验位进行逻辑“异或运算”得出)对应表示为G1、G2、G3、G4,……,正常情况下均为0。
            2)校验码计算示例
            同样举上面的例子来说明,码字为??1?001?1101。
            先求第1个“?”(也就是p1,第1位)的值,因为整个码字长度为12(包括信息码长和校验码长),所以可以得出本示例中p1校验码校验的位数是1、3、5、7、9、11共6位。这6位中除了第1位(也就是p1位)不能确定外,其余5位的值都是已知的,分别为:1、0、1、1、0。现假设采用的是偶校验(也就是要求整个被校验的位中的“1”的个数为偶数),从已知的5位码值可知,已有3个“1”,所以此时p1位校验码的值必须为“1”,得出p1=1。
            再求第2个“?”(也就是p2,第2位)的值,根据以上规则可以很快得出本示例中p2校验码校验的位数是2、3、6、7、10、11,也是一共6位。这6位中除了第2位(也就是p2位)不能确定外,其余5位的值都是已知的,分别为:1、0、1、1、0。现假设采用的是偶校验,从已知的5位码值可知,也已有3个“1”,所以此时p2位校验码的值必须为“1”,得出p2=1。
            再求第3个“?”(也就是p3,第4位)的值,根据以上规则可以很快得出本示例中p3校验码校验的位数是4、5、6、7、12,一共5位。这5位中除了第4位(也就是p3位)不能确定外,其余4位的值都是已知的,分别为:0、0、1、1。现假设采用的是偶校验,从已知的4位码值可知,也已有2个“1”,所以此时p2位校验码的值必须为“0”,得出p3=0。
            最后求第4个“?”(也就是p4,第8位)的值,根据以上规则可以很快得出本示例中p4校验码校验的位数是8、9、10、11、12(本来是可以连续校验8位的,但本示例的码字后面的长度没有这么多位,所以只校验到第12位止),也是一共5位。这5位中除了第8位(也就是p4位)不能确定外,其余4位的值都是已知的,分别为:1、1、0、1。现假设采用的是偶校验,从已知的4位码值可知,已有3个“1”,所以此时p2位校验码的值必须为“1”,得出p4=1。
            最后就可以得出整个码字的各个二进制值码字为:111000111101(带阴影的4位就是校验码)。


            IP属地:上海6楼2017-03-20 22:04
            回复
              太棒辣!


              IP属地:安徽来自手机贴吧7楼2020-09-05 11:56
              回复