原码,反码,补码,阶码,移码是什么?有什么区别(讨论机器数的表示)

原码,反码,补码,阶码,移码是什么?有什么区别(讨论机器数的表示)

本文内容参考自王达老师的《深入理解计算机网络》一书<中国水利水电出版社>

一、机器数解释:

机器数是计算机中参与运算且带有(+)、(-)属性的二进制数
–引用自王达老师的《深入理解计算机网络》

  • 1、计算机中的数如何表示符号?(计算机的数也是存在正负之分的,那么如何区分它们的正负呢?)
    我之前的博文介绍二进制的时候,是没有考虑二进制带符号的情况,但是,数制之间的转换是不包括符号位的,即转换的时候,符号位是保持不变的。现在,我专门来介绍带符号的二进制数,也就是机器数的各种表示(原码,反码,补码–正是因为二进制带上了符号,所以产生了这些叫法)
    • ⑴、机器数 –机器数是有符号位的二进制数,规定用它的最高位表示符号位,0表示正数,1表示负数,比如+1101:01101,-1101:11101,一般来说,没有符号位的二进制数称为"无符号数",是不参与各种运算的,比如信息编码等。
      这里需要提示一下:一旦二进制数有符号了,那么在数制之间进行转换的时候,千万记得不要把二进制最高位的数值表示一起转换了,因为那是符号位。也不要对0 1101 0110这种开头为0的二进制数感到奇怪,这个最高位的0不是二进制的数值有效位,仅表示符号(+),说明了这是一个正数。
      下面,我们来谈几个必须要了解的概念:

      以下内容引用自王达老师《深入理解计算机网络》一书
      真值:因为机器数是带有符号位的了,那么这个机器数的实际值是需要将符号位去除的,剩余的部分才是这个机器数的实际值,这个剩余的部分所表示的值就是机器数的"真值"。
      字长:字长可以简单理解为用来表示一个机器数所用的二进制位数,就是我们常说的8位,16位,32位,64位(硬件CPU能处理的最小字长是一个字节,一个字节就是8位二进制,所以也会说1字节,2字节,3字节,4字节)。
      如果确定了字长,那么所有的机器数都是用固定的字长来表示,无论机器数的大小。
      字长不同,同一个机器数的表示就不同,比如字长为8位,+5的表示为:00000101,-5的表示为10000101,16位字长,+5的表示为0000 0000 0000 0101,-5的表示为1000 000 000 0101。一个机器数是必须要单独占用一个字长的,否则计算机无法知道一个存储单元中的机器数到底是多少。
      +0和-0: 虽然说,我们在纸面上看+0和-0是一样的,但是因为机器数的符号位正值用0表示,负值用1表示,所以在计算机中+0和-0的表示是不同的。如果是32位字长,+0是0000 0000 0000 0000 0000 0000 0000 0000,-0是1000 0000 0000 0000 0000 0000 0000 0000。所以,在二进制的机器数中,0有两个(+0和-0),且他们的表示形式不一样。
      必要的计算机知识:CPU有16位(目前比较少见了),32位,64位的,内存也有16位(目前比较少见了),32位,64位的,这里的16位,32位,64位对应的就对应了相应的计算机系统中的字长,计算机内存中数据的存储是以单位长度(字长)进行连续存储的,一个机器数只能在一个存储单元中进行存储(一个存储单元的长度就是一个字长),反过来,一个存储单元只能存储一个机器数,CPU也是以存储单元(一个字长)为单位从内存中读取并处理数据的,所以,字长是计算机一次可处理的机器数的码位长度,是计算机进行数据存数和数据处理的运算单位。我们通常所指的32位处理器就是指该处理器的字长为32位(一次能处理32位二进制位),64位处理器也是同理。显然,字长越长,CPU处理数据的能力就越强(因为字长越长,能表示的数值越大,同时能包涵的数据内容也就越多)。
      目前的计算机大多是64位的,我们知道,8位是一个字节,也就是说,现在计算机中的一个存储单元是8个字节,那么,64位的CPU可以一次性从内存中读取并处理8个字节。

  • 1、机器数的编码形式?
    为了使机器数的运算方法适用于所有机器数("+"、"-"等运算),同时保证运算不会出现二义性,在机器数的运算方法演变过程中出现了三种不同的机器数编码方式:原码、反码、补码。
    • ⑴、原码
      原码是一个数的带符号位的二进制表示,但是原码与字长有关,比如+3的原码是011(不考虑字长的情况,0是表示正数,11是3的二进制表示),如果字长为8位,那么+3的原码就是0000 0011,需要用0补齐8位,同理,-3的原码是1000 0011(只需要将+3原码的符号位由0改成1),如果是浮点数的二进制,则需要在小数点右边补0,如(0.11010)B,它的8位字长原码为(0.1101000)B,在右边末尾补0直至长度为8位。
      虽然说原码很直观,设计简单,容易理解(10进制直接转换过来的二进制表示,其实就是原码),但是在某些方面,原码还是有不足之处,总结起来就是以下两点:
      1、原码的加减运算规则很复杂,它的符号位要进行单独的处理,还可能出错,采用原码直接进行机器数运算的时候,两数同为正数或者是负数时,运算是没什么问题,如果一个是正数,一个是负数,那么两数相加减就会出问题:
      0 0000001+1 0000001 = 1 0000010(-2);
      以上例子是将十进制的1+(-1)转换成二进制进行运算,结果应该是等于0,但是用原码运算,得出的结果是-2,显然不正确!(二进制的加减运算规则是0+0=0,0-0=0,1+0=0,1-0=0,1+1=10,1-1=0)。
      2、原码中的0有两种表示(+0和-0),存在二义性!
      所以从以上两点来看,直接采用原码进行机器数运算是不合适的!
    • ⑵、补码
      因为原码在机器数运算时有很明显的不足,经过坚持不懈的努力,设计者终于找到了一种能解决原码所有不足的另一种新的编码方式–补码!那么补码又是一种什么编码呢(补码不可能采用与原码一样的编码方式,因为那将会导致与原码一样的不足)?
      补码的编码规则其实是建立在原码的基础上的,内容如下:
      1、正数的补码和原码完全一样,负数的补码是通过将除符号位外的其他各位取反,再将结果+1得到的(计算负数补码的时候,一定要隔离它的符号位,保持不变!)。
      比如一个原码为0 0000101的机器数,因为它的最高位是0,所以是个正数,它的补码是0 0000101(与原码完全一样)。
      另外一个原码为1 0000101的机器数,因为它的最高位是1,所以是个负数,它的补码是,先把最高位1隔离开,剩下的部分为0000101,对这一部分取反(取反的意思就是1变0,0变1)得到的j结果为1111010,再将1放回最高位,所以最后的结果是1 1111010,。
      补码的最大优点是方便了机器数的运算,补码运算的时候,符号位是不需要单独处理的,可以将符号位一起带入运算。同时,它也继承了部分原码的优点。
      以字长为8位为例,将之前的1+(-1)转换成二进制补码运算:
      1的补码为:0 0000001
      -1的补码为:1 1111111
      那么0 0000001+1 1111111 =100000000,得到的结果100000000明显是9位了,但是这里的字长只有8位,所以将舍弃最高位1,得到的结果就是00000000,转换成十进制就是0,正确!这里需要特别注意,当补码计算得到的结果超出了字长,那么需要舍弃最高位,取结果的字长长度,才能保证正确!
      同时我们还发现:
      以字长为8位为例
      +0的补码是:0000 0000
      -0的补码是1 0000 0000这里也同样是超出了8位字长,舍弃最高位后-0的补码也是00000000,所以,补码中没有+0和-0这种二义性!
      如果给你一个补码,想得到原码,那么你只需要通过原码转换成补码的逆过程就可以了。
    • ⑶、反码
      通过以上的介绍,我们可以得知,补码是二进制运算中的最佳选择,因为它可以将符号位和真值一起进行运算,完美解决了异号二进制数之间的加减运算问题,那么,反码是怎么出现的呢?
      实际上,反码只是原码向补码表示形式转换过程中的一个过渡形式,不是可直接用于机器数运算的一种编码方式,当初能发现反码这种形式,是因为很多时候一件事情从一个方向解决不了,往往会考虑另一个角度去解决,即原码解决不了运算的问题,那么所有的二进制取反是否可行,就这样,偶然发现了反码这种表现形式。
      其实,与补码一样,反码也是专门针对负数的,因为正数的反码也是和原码完全一致,而对负数,则需将除符号位外的其他位取反,即由原来的0变1,由1变0。
      因为反码只是单纯地对每一位取反,所以它也存在某些不足:
      1、1+(-1)采取反码计算的结果与原码一样是不正确的。
      2、2+(-3)采取反码计算的结果却是正确的。
      3、123+(-121)采取反码计算的结果与原码一样是不正确的。
      反码中也有+0和-0之分。存在二义性。因为从原码的角度来看,+0和-0的反码不一样。
      原码、补码、反码总结:正数的原码,反码,补码都是一样的,而负数的原码,补码,反码是不一样的,需要分别转换得出。负数的补码是除符号位的其他位取反再+1得到,负数的反码是除符号位,其他位取反得到。
    • ⑷、阶码
      阶码是机器数里浮点数中的称谓,阶码指的就是浮点数中的指数,它表示了浮点数中这个小数点的具体位置(计算机中不是直接存储二进制浮点数的字面值,而是通过存储浮点数的符号位,指数[也就是阶码],尾数值来间接存储一个浮点数的,因此会导致浮点数的小数点位置不确定,在我的其他博文中也有有关这个问题的详细解释)。
      下面来举个例子:
      对于任意一个二进制数N,均可用N = S * 2P表示,其中S就是尾数,P就是指数,也叫阶码,2为指数基数,也叫阶码的底数,P,S都用二进制表示,S表示N的全部有效数字,所以在这里,P能指明小数点的位置,当阶码固定,表示的数就是定点数,当阶码可变,表示的数位浮点数,这就是阶码的主要应用。
      其实在我看来,阶码就是一个称呼而已,没必要深究,只是如果你看到了阶码这两个字,你能知道,它指的就是指数就行了。
    • ⑸、移码
      移码有两个很重要的作用:
      1、方便机器数比较大小。
      2、用于浮点数中"阶码"的修正(实际上就是为了将浮点数的表现形式都规范化)。
      以上内容的实现都是通过在原来编码的基础上加上一个常数来实现的(实现方式不一样),下面具体解释一下。
      在机器数比较方面的应用
      机器数的运算都是采用补码的方式,因为采用补码不必考虑符号位,符号位可以与真值位一起带入运算,很方便,但是,如果你直接通过补码来比较机器数的大小,不是很直观。
      比如十进制+31,对应的二进制表示是+11111,也就是011111,它的补码为011111,十进制数-31,对应的二进制表示为-11111,它的补码为:100001,看上去好像100001》011111,实际上正好相反,如果我们对每个机器数的原码真值部分(符号位需要转换成"+“或者是”-")加上一个2n,(n代表原来二进制编码的真值部分位数,即去掉符号位剩余的位数,在这里是5),所以用31的真值部分+11111+25 = 111111,而-31就是-11111+25=000001,然后将得到的结果再比较大小,那么两个数的大小就一目了然了。
      移码的计算公式:X移 = X + 2n,因为阶码通常是原码形式,由此可以得出移码与补码的关系,移码实际上就是补码的符号位取反,其他位不变。这样一来,在移码中,正数的符号位为1,负数的符号位为0,与前面的原码,补码,反码中的符号位规定相反。
      在浮点数中的应用
      阶码就是指数,但是指数也是有正负的,这样一来,就会导致一个机器数存储需要有两个符号位:一个是机器数本身的符号位,一个是阶码的符号位。
      为了节省空间,省去阶码的符号位,就采用了对每一个阶码都加上一个相同的,比较大的正数(偏移值),这样就能保证最后的结果都是正整数,同时在运算的时候,系统会自动对指数部分减去这个正数,以此来保证计算结果正确,以上过程中,“阶码"就变成了偏移了的"阶码”,就是移码了。有的资料里面也会称为"增码"。
      这个正数是根据存储器的字长来规定的,具体的可以自行了解一下。
      在此,再次感谢王达老师的《深入理解计算机网络》一书对本文的启发!

PS:时间有限,有关计算机基础的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正!

猜你喜欢

转载自blog.csdn.net/ruidianbaihuo/article/details/87875178