数据的表示和运算(计组)

一、数制与编码

        我们的日常生活中用的都是十进制逢十进一,而计算机中通常用二进制来表示逢二进一,我在学习C语言的时候就接触到了计算机世界的原码、反码、补码和移码;有符号数和无符号数的概念。这里简要说明一下这些名词的意义。

        有符号数:可以表示正负的数,在计算机的二进制编码中通常用最左边的位来表示符号;所以也称这位二进制数为符号位,符号位1为负;0为正。

        无符号数:不管正负号,更没有符号位一说,比方说-1的符号位为1,而无符号数就会将这一个符号位看作是数值。

        原码:正数的原码反码补码都是一样的,直接将十进制的数转化为二进制就是该十进制数的原码、反码以及补码。

        反码:反码就是将原码除符号位其他位按位取反即可得到反码,只有负数才有这种变换机制

        补码:在反码的基础上加一,只有负数才有这种变换机制。

        移码:将补码的符号位取反,正负数都有这种变换机制。如果按照IEEE要求的话,IEEE为了使各个计算机的移码变换规律相同会统一一个偏置值,而移码就变成了移码=真值+偏置值。

        1.1进制之间的转换

        我比较讨厌教材化的方式来转化各种进制,这里我说一下我常用的方法,更加简单而且也不易出错。

        十进制->二进制:我们将二的各种次方都写在草纸上来凑出十进制数即可求出二进制数,比如我们要求51的二进制数,51=2^5+2^4+2^1+2^0,这样一来我们就可以得出二进制数为110011,也就是将2进制数从右向左从0开始编号,2^0代表0号位为1,2^1代表1号位为1,而2号位并没有在等式中,所以2号位为0。

        八进制、十六进制->二进制:我们只需要记住,每一位八进制数代表3位二进制数,每一位十六进制数代表4位二进制数即可。如果位数不够的话,整数情况在高位补0,小数情况在低位补零(二进制编码左高右低)。

        真值和机器数:日常生活中带有正负号的数就是真值,而原码、反码、补码就是机器数。

        定点小数和定点整数:在十进制->二进制中我们知道了整数如何变换,小数同理,比如0.75=2^-1+2^-2,那么其二进制数为0.11。我们注意到了0.11中有一个小数点,而这种含有小数点的只有小数部分的数称为定点小数,我们前面求出的110011其实也是110011.只不过我们通常将这个小数点忽略,对于定点小数来说符号位是小数点前一位,对于定点整数来说最高位是符号位。重要的是我们要清楚0.11是定点小数,110011.是定点整数,而110011.11什么都不是。

        学到这里,其实还是存在许多问题的,比如如果我们的二进制数很大,寄存器中放不下怎么办?还有拿51举例,如果按照我们前面的想法,二进制最高位表示符号位,那么110011表示的就是某个负数而不是51。我将在编码表示范围中说明。

        1.2原码、反码、补码、移码的表示范围

        当计算机要实现计算功能的时候,需要CPU中各种寄存器与ALU的配合,而寄存器的空间是有限的,ALU的接收能力也是有限的。那么也就是说其实存放在寄存器中的二进制数是有一个表示范围的,其范围不能超过机器字长。以原码来说,如果寄存器能够存放8个bit,那么去掉首位的符号位后,能够表示的值的范围为 - (2^7 - 1) ~ 2^7 - 1 ,这个范围的来源是,当符号位分别为0和1时数值为从全0到全1表示的范围,那么这里有一个要点是00000000和10000000都代表着0;反码一样。

        那么对于补码,按理来说其表示范围也应该是 - (2^7 - 1) ~ 2^7 - 1,但是在补码中0的表示范围是唯一的那就是:00000000。那么对于10000000这个编码表示的数值是什么呢?我们试着将该补码转化为原码,我们发现原码是00000000而最高位是1由于取反加一的操作导致寄存器装不下这个9位的二进制数导致溢出,那么造成这种状况的原因是什么呢?原码范围为- (2^7 - 1) ~ 2^7 - 1,那么也就是说我们在将补码转换成原码的过程中超过了这个范围,而且刚好超过了1,也就是-128转化为128的时候,由于正数的最大表示范围为127,导致的溢出。

        综上,补码的表示范围为 - 2^7 ~ 2^7 - 1,而移码是对补码的符号位取反,所以移码与补码相同。对于无符号数来说,由于不存在符号位一说,所以其表示范围为0~2^8-1。

        二、原码、反码、补码的加法运算

        我们要知道,在我们的计算机中都是用补码来运算各个数据的,那么我们如果反其道而行之,我们想计算8+(-7)的原码,假设机器字长为8,那么就是00001000+10000111=10001111,这个结果的十进制显然不为1。所以,当原码出现负数的时候,我们显然用原码来直接进行计算是行不通的。但是对于补码来说8+(-7)=00001000+11111001=100000001,但是由于机器字长为8,所以最高位的1被丢弃,结果为00000001,符号位为正,原码为1,答案正确。

        所以,用补码来计算,无论加数与被加数的正负如何,直接令其补码相加即可,这也就是为什么计算机计算的时候都是用补码来计算(这里要强调一点,计算的时候用补码,但是表示外存地址的时候用的是无符号数)。

        三、电路的基本原理

        计算机的运算过程就是通过电路中的各种元件来完成的,常用的元件有:与门、或门、非们、与非门、或非门、异或门、同或门。与门类似于C语言中的&&,只有当两个数值都为1的时候才能是1,否则是0;非门类似于C语言中的||,只有当两个数值都为0的时候才能是0,非门就是将结果取反,异或门是指当两个数相同时为0,相反时为1;同或门是指两个数相同为1,相反为0。

        而2进制数中的每一位要么是1,要么是0,这就恰好满足了这些元件只能表示两种状态1/0的要求。那么如何使用这些元件来实现二进制数的运算呢?

        3.1一位全加器

         以图中运算为例,最初的C0默认为0,那么如果我们想算本位Si只需要使A、B、C进行异或即可,因为如果有两个为1,那么异或后一定为0,如果是三个1,异或后为1,所以异或可以求出本位的和,而如果我们要求C的话,令A与B相与,这样一来,A和B都为1时,C才能是1;还有另一种情况,当A和B只有一个是1的话,A异或B后为1,接下来当C为1的话,再与其相与,C就是1。这样一来本位和进位都能求出,就可以算出所有的二进制加法。图中的电路既是我所描述的逻辑运算。

        3.2并行进位加法器

        在一位全加器中我们了解到,如果我们想算出本位的值S,以及本位要进位的值C,就需要上一位进位的值C,这就导致了,我们的机器字长虽然足矣一次性将整个二进制数传输进寄存器中,但是由于一位加法器的性质,就导致了我们必须从低位一位一位的向高位计算,效率十分低下。那么我们就需要解决这种计算本位需要上一位进位导致的计算效率被拖住的问题,于是并行进位加法器就诞生了。

        我们要知道问题到底是出在了什么地方,是计算本位需要上一位的进位拖慢了整体的计算速度,那么并行进位加法器的解决方法就是利用多个一位全加器一次性的算出多个进位,实现方式涉及到递归思想,比如,我们要算第四位的进位C4,那么根据上图中的公式可知C4可以用C3、A4、B4来表示,而C3可以用C2、A3、B3来表示,C2可以用C1、A2、B2来表示……,这样递归下去就可以根据C0直接将多个进位利用多个一位全加器并行计算的方式一次性的全部算出,这样就可以大大提升由于上一位进位导致的计算效率降低的问题。

        3.3补码加减运算器     

         我们前面讲过补码的加法,以及为什么要使用补码来进行算数运算,这里就可以用补码的加法来表示补码的减法,比如[8]补 - [7]补=[8]补 + [-7]补,计算机实现方式如图:

         被加数直接进入加法器即可,重要的是加数,我们注意到图中有一个电路元件叫做多路选择器,多路选择器的作用就是利用sub这个信号来区分加法以及减法,并将减法转化为加法,实现方式为:加法直接进入加法器,多路选择器不做处理;而减法则进入非门,将加数的补码全部位取反再加一,就得到了加数的负数的补码,就可以使被加数加上一个负数来实现加法到减法的转变。

        四、溢出判断

        4.1利用符号位判断是否溢出

        一位符号位判断:我们利用加数和被加数以及结果的符号位,三个符号位相与,结果为0无溢出,结果为1则有溢出。

        根据进位情况进行判断:采用数值位的最高位的进位与符号位的进位相异或,若结果为1则有溢出,结果为0则无溢出。

        采用双符号位判断:若双符号位为00表示正数无溢出;若双符号位为01表示正溢出;若双符号位为10表示负溢出;若双符号位为11表示负数无溢出。此时最高位符号位代表真正的符号。

        4.2利用标志位判断是否溢出

        前面我们讲过了补码加减运算器,而该运算器在运算完成后会保存4个标志位,如图:

         OF是针对有符号数来说的,当OF为1时表示溢出,OF为0时无溢出。OF的实现方式与利用进位情况来判断溢出的方式相同,数值位最高位的进位异或符号位的进位。这里我们思考一个问题,00时不溢出,11时也不溢出,而10和01都会溢出,那这个规律是不是和双符号位判断溢出的规律及其相似?

        其实学到这里我希望你能思考一件事情:假设某个寄存器中最多能存放8个bit位,按照我们的惯性思维来看,只要数值超过8个bit位,那么就会溢出,但是实际上,符号位和数值最高位进位为11时,已经有数值溢出寄存器了,但是为什么在最后的原码结果上来看是正确的无溢出的呢?虽然我没学过数论等关于数字的知识无法解释这一现象,但是我发现了一个规律:补码的加减运算中,只要数值最高位和符号位溢出后,不改变原本理论上的的符号位,那么就代表着该计算没有溢出,最终的结果转换为原码后是正确的。我们来好好分析一下这句话:00时显然没有改变符号位,所以没有溢出;11时虽然符号位进位为1改变了符号位原本的数值,但是数值最高位进位也是1,这就导致了符号位又恢复为原来的数值,所以11也没有溢出;10时符号位进位为1改变了原本的符号位数值,而数值最高位进位为0,无法将符号位的数值恢复,导致符号位改变,所以10溢出了;最后是01,虽然符号位没有进位,但是数值位进位为1,就会导致改变原本的符号位,所以01也溢出了。

        其实我们并不需要知道理论上原本的符号位应该是1还是0,我们只需要关注这个数值最高位和符号位进位的过程会不会改变它原本的数值即可。

        以上的逻辑是我个人猜测,实践中也印证了我的想法,而该猜测的所有想法都基于会不会改变原本理论上的符号位,而这也印证了,OF是针对有符号数的,对于无符号数是没有意义的。

        SF:SF就是输出结果的最高位,其实就是符号位,用来表示结果的正负。所以SF也是针对有符号数的,对于无符号数没有意义。

        ZF:表示含义为运算结果是否为0,如果运算结果所有位都为0,那么ZF就是0,这里也要回忆一下,补码的0只有所有位都为0这一个表示方法。

        CF:CF是针对无符号数的标志位,CF等于最高位的进位异或sub的值,sub加法为0减法为1。CF对于有符号数的加减法无意义。

        五、乘法运算(重点难点)

        5.1移位运算

        移位运算是乘法运算的基础,我们在计算十进制数的时候,通常会移动小数点来代表着对该十进制数乘多少个10,小数点右移一位就是乘10,那么二进制同理。定点数在机器中存储,虽然小数点不能移动,但是我们可以移动二进制数,这样就在逻辑上达到了移动小数点的目的。

        补码移位运算溢出判断:移位运算溢出判断也是像我前面说的一样,左移位后,原本理论上的符号位如果不改变,那么就没有溢出,比如符号位与数值最高位是11或者00。

        原码的移位:原码左移一位相当于小数点右移一位,就相当于对整个数乘2,我们在移位的时候,低位和高位可能会出现缺失的情况,所以我们要补0,而如果高位或者低位溢出的值为1,那么就会出现误差。

        反码的移位:反码的移位同原码一样,不过缺失的情况需要补1。

        补码的移位:补码的移位也同原码一样,不过高位缺失补1,低位缺失补0。

        逻辑移位:逻辑移位高位缺失和低位缺失都要补0。

        循环移位:循环移位没有缺失这一说,因为不管是左移还是右移,溢出的那一位会补充缺失的那一位,如果是带进位位的循环移位,还会将最高位的进位一起算进这个循环移位中。

        那这里简要复习一下循环队列:循环队列最初的情况是两个指针不能相遇,所以会舍弃一个结点,解决的办法有两种一个是设置计数器,另一个是设置一个tag标志。

        5.2原码的乘法运算        

         我们想要知道CPU是如何进行原码的运算之前,我们需要复习一下CPU中都有哪些需要用到的元件:1.ACC累加器:累加器顾名思义就是每次运算都需要加上某个数;2.MQ乘商寄存器:乘商寄存器顾名思义就是存放乘数和商的寄存器;3.通用寄存器:通用寄存器通常用来存放被乘数和除数;4.ALU算术逻辑单元:用来进行算术运算和逻辑运算的单元。

        以上这四种元件与ALU的配合贯穿了原码、补码的乘除法运算的始终。这几个元件的命名都很有逻辑,在接下来的学习中我们会更加清晰的认识这几个元件是如何互相配合着运算的。

        上图是十进制的乘法运算,我们会发现,乘数的个位与被乘数相乘后得到的数放在本位,乘数的十位与被乘数相乘后得到的数需要左移一位。而这个左移的特点就与原码的乘法在CPU中运行的机制不谋而合。那么这三个数985、985、1970在进行了左移后又进行了相加的操作,那么根据我们CPU中寄存器的命名方式,显然这个每次累加的结果要放在ACC这个累加器中的,而最初ACC中是没有数值的,所以ACC初始值为0,接下来我们要看MQ,这两个数中,0.211是乘数,所以要放到MQ乘商寄存器中,接下来我们看通用寄存器,现在看来只剩下被乘数0.985没有放入寄存器,所以理所当然的,0.985应该放在X通用寄存器中。

        知道了这些数应该放到哪些寄存器,我们就要开始模拟计算机是如何进行运算的了,如图:

         各种前提要求图中已经讲的很详细了,我们直接描述过程。由于不管符号位所以最开始ACC中的值全部置为0,MQ中存放01011,X中存放01101。我们可以看到MQ中有一个bit是黑色的,我们就是要根据这个黑色位置的值来判断我们接下来要在ACC中加上一个什么值,因为根据我们平时的乘法运算习惯也是先用乘数的个位来乘以被乘数,而这个黑色的位置目前来说就相当于乘数的个位,我们看到该位上的值位1,所以我们要使1乘以被乘数加到ACC中去,也就是加上被乘数。接下来,按照我们的乘法习惯,要用乘数的十位来与被乘数相乘,再将该部分积加到ACC中,但是此时的黑色位置存放的是乘数的个位,所以我们要将ACC和MQ看成是一个整体,然后将该整体向右移动一个bit,这样一来原本乘数的个位就被顶出寄存器,而乘数的十位顶替了乘数原本个位的黑色位置,而ACC的高位缺失一位需要补0。接下来我们照猫画虎就可以完成刚刚的运算过程。

        刚刚的运算过程中,我们将ACC与MQ一起向右移动了一个bit,这样一来乘积的低位就被移动到了MQ中,而这个低位就将MQ中原本的乘数的个位挤了出去,这也就是为什么在最终结果看来ACC中存放的是乘积高位,而MQ中存放乘积低位。现在我们根据这个过程在反过来看看这些寄存器的名称,是不是觉得这些寄存器的命名真的很有逻辑?

        最后,当这个运算过程循环了n次后,MQ中就只剩下了乘积的低位以及一位乘数的符号位在那个黑色的位置上,至此整个循环结束,ACC和MQ中的值就是最终结果的绝对值,而我们再根据乘数和被乘数符号位的异或来判断最终结果为正还是负。

        了解了机器的运行机制后,我们还需要了解手写的方式如何来描述原码的乘法运算,如图:

         图中左边的算式为ACC中累加以及移位的过程,图中右边虚线的内容是MQ中移位的过程,我们观察到虚线部分的内容最初只有4位小数,这是因为原码的乘法运算符号位并不参与运算。最初的时候ACC中值为0,我们观察虚线左边最近的一位小数,若其值为1,则在ACC中加上一个被乘数的绝对值,若其值为0则加0,然后ACC和MQ都进行移位操作。原码的乘法中规中矩,没有什么特别需要注意的点,熟悉这个过程即可,接下来我们看补码的乘法运算。

        5.3补码的乘法运算  

        补码的运算中,二进制数的存放位置与原码的乘法相同,而与原码不同的是,补码的乘法运算符号位也参与运算,并且每次ACC中累加的数值是由乘数的最低位与辅助位共同决定的,如图:

         由于补码的运算涉及到了符号位的运算,所以在右移的过程中,需要进行算术右移,也就是根据符号位来进行补充,不能像原码那样直接逻辑右移,接下来我会描述该运算过程。

        首先还是ACC中全部位置为0,MQ中的辅助位也置为0,之后查看MQ中的辅助位-最低位=-1,所以ACC中需要加上x绝对值相反数的补码,累加完毕后,ACC和MQ都进行算数右移,原来的辅助位溢出,由原来的数值位最低位顶替,这样一来此时的辅助位=1,数值位=1,辅助位-数值位=0所以接下来ACC中将加上0,接下来的步骤也就照猫画虎即可,最终得到的双符号位就可以代表着结果的正负。接下来我们看手算的图示:        

        图示很全面,学到这里应该可以自行看懂,不做赘述。 

         5.4原码的除法运算(恢复余数法)(加减交替法)

        上图为我们手算二进制出发的过程,两个二进制的数相除,每次得到的商要么是1要么是0,因为二进制的每一位只有两种状态,如果除数比被除数大的话,除数不够除,那么就要借位然后商0,如果除数比被除数小,除数够除,而且两个位数相同的二进制不可能发生其中一个大于另一个的二倍的情况,所以如果除数比被除数小,那么就会商1。知道了这个基本原理,我们来模拟实现一下机器是如何实现原码的除法运算的。              此处我们看图就可以总结一下了,原码的运算都是不包含符号位的,需要单独处理符号位;而补码的运算都是包含符号位的,最终结果可以通过符号位来反应其正负。

        根据寄存器的命名我们可以知道,MQ乘商寄存器用来存放商,ACC累加寄存器用来累加,那么此处的ACC就会用于进行每次求出一位商后得到的余数再与除数相减,这里要注意,由于我们在计算中不考虑符号位,所以此处均为正数,原码、补码都是相同的,这里需要减去一个值就得转换成加上该值绝对值相反数的补码。一定要分清我们的不考虑符号位,以及这里的带着符号来进行运算的区别!!!虽然这里用到了符号位但是整体看来是不考虑符号位的。

        知道了如何用余数减去除数之后问题就简单许多了,首先计算机很傻,没有设计比较大小的电路,不会区分除数和被除数的大小,那么此时计算机只会默认商1,那么计算机此时就是默认为被除数大于除数,所以当ACC加上除数绝对值相反数的补码之后,如果ACC中的值为负,那么就代表着实际上被除数小于除数,也就是代表着计算机一开始的判断是错误的,那么计算机是一个知错就改的好孩子,所以计算机此时知道自己上商上错了,那么他就会将原本上在MQ最低位中的1改为0,虽然解决了MQ中的问题,但是ACC中已经进行了一个错误的减法,那么我们就需要将ACC中的值恢复原样,也就是再将除数重新加到ACC中,而ACC中存放的就是余数(在计算的开始我们也称之他为被除数)这种将余数恢复为原样的计算方法叫做恢复余数法,每次得到一个商后就将MQ和ACC中所有的值逻辑左移,因为接下来的商还要放在MQ的最低位。然后继续按照这种计算机默认商1,然后再进行判断自己是否商错,直到结束后,MQ中存放的就是最终的商,而ACC中存放的是余数。接下来我们看如何用手写的方式实现这个过程,如图:

         首先计算机默认商1,但是商1后得到了1.1110是一个负数,所以计算机将商1改成商0,并使余数恢复,回复完毕后第一个商计算完毕,将余数左移计算下一个余数直到结束。

        我们会发现,恢复余数法中,每次商错均要再恢复余数,那么有没有什么办法可以跳过恢复余数的过程,直接开始下一步计算呢?答案是肯定的,计算机每次商错的时候,可以直接使该“错误的”余数左移再加上除数即可直接开始下一个商的判断。该方法俗称加减交替法。

        5.5补码的除法运算(加减交替法)

        补码的运算都是符号位也参与运算的,最终得到的结果的符号位可以表示最终结果的值,补码除法上商的规则有所改变,如图:

        补码的除法上商规则为,被除数/余数的符号位如果与除数相同则商1,否则商0,不同于原码的除法运算需要比较被除数/余数和除数的大小来判断商1还是商0,补码的除法更加简便,其直接判断符号位即可,电路设计起来也简单,所以补码的除法中,计算机是可以直接判断商1还是商0的

        根据图示,首先计算机判断,被除数和除数异号,所以商0,并使被除数+除数,第一步上商结束。第二步发现余数和除数同号,所以商1,并使被除数-除数,第二步上商结束。最后一步不用再进行判断是否异号,直接商1即可,误差很小可以忽略。

        5.6原码、补码的除法运算比较       

         上图为本节除法运算的区别,我们可以看到原码的运算符号位都是不参与运算的,而补码的运算符号位均参与运算。在加减交替法中,原码和补码都是加减次数与机器字长相同的,而原码可能在最后一次运算的时候默认商1,但是此时实际上应该商0,但是由于已经达到了寄存器的存储上限,不可以再进行下一步上商,故不能继续加减交替,所以应该按照恢复余数法来为余数进行一次恢复的操作,所以原码除法根据最后一步上商的正确与否,得到的加减次数也不同。

        C语言强制类型转换:该知识点学过C语言的应该都会,这里只特殊强调一下,当长类型转化为短类型时,寄存器中保存低位二进制编码。

        数据的存储与排列:该部分内容需要注意的有两点。

        1.数据存储的大小端方式:比如地址从左到右,从低到高排列,而数据存放按照从高位到低位来存放,也就是高位存放在低地址处,此为大端存储;而数据低位存放在低地址处,此为小段存储。计算机中地址存放方式就是小端存储。

        2.计算机中数据有两种存储与排列方式,第一种是为每个数据类型都按照一个字长来为其进行边界对齐操作,如图:

        

         该例子在C语言的结构体中表现得淋漓尽致,我们在写C语言的时候,当我们定义一个struct结构体的时候,如果不按照先定义短类型,再定义长类型的方式的话,会大大增加结构体的长度,因为现代计算机大多都采用这种边界对齐的方式来进行数据的存储。

        六、浮点数

        6.1什么是浮点数

        在C语言中,有各种各样类型的变量,char、int、float、double……这些变量可以表示的范围依次增大,所占的bit也依次增大,但是int和float明明都占32个bit位,但是为什么float的表示范围比int大那么多呢?这是因为int的二进制数组成为符号位+数值位,也就是除了符号就是数值;而float的二进制数组成中包含了一个可以表示科学计数法中次方的二进制数,而这个可以表示次方的二进制数我们称之为阶码,float在现代计算机的组成为符号位+阶符+阶码+尾数。这种在二进制编码中含有的表示次方项是浮点数最基本的标志。

        6.2浮点数的基本组成与要求

        前面我们讲过,浮点数包含符号位、阶符、阶码、尾数,而这几种二进制数都有各自的要求(这里我们先不谈国际标准IEEE)。

        我们来将浮点数的这四个二进制数用我们熟知的科学计数法来翻译一下就是,符号位代表正负、阶符代表次方项的正负、阶码代表次方项的真值、尾数代表前面的那个小数,但是这里有一个问题,科学计数法中都是一个小数乘以10的多少次方来表示的,但是浮点数中小数,次方,符号都包含了,唯独没有10。所以浮点数中其实还隐含了一项,那就是基数,基数就是我们所熟知的科学计数法中的10,而十进制中基数是10,那么二进制中这个基数就是2。

        了解了浮点数的基本组成后,我们来了解一下组成浮点数的这几个二进制数都有什么要求,符号位没什么好说的了,这里要说一下阶码,其实阶符和阶码共同组成了阶码,而阶码需要用移码来表示(移码只能表示整数)。我们所熟知的移码通常是通过对补码的符号位取反来计算的,但是在这里,我们要通过另一种方式来达到计算移码的目的。 

        移码=真值+偏置值,在移码的最本质的计算中,这个偏置值通常取128也就是10000000,而真值就是二进制的科学计数法中的那个指数的值,通过这种方式计算的移码就是我们通过对补码的符号位取反所得到的移码。但是在国际标准IEEE中偏置值取127也就是01111111,那么当题目给出一个移码后,我们想求出其对应的次方,我们就可以令移码减去127,也就是加上127相反数的补码即可。

        最后是尾数,尾数就是科学计数法中的小数,了解科学计数法的我们知道,在科学计数法中,通常是小数点前有一位有效数字,而该有效数字不能是0,这样才能保证科学计数法的精度达到最大,那么如果小数点前那个数字偏偏不是0,我们通常会使小数点向后移动到第一个非零数字之后,同时改变阶数,而这个过程叫做规格化,在二进制的世界中,我们虽然不能移动小数点,但是我们可以通过移动数字的方式来进行规格化,当小数点前的那个数字是0的时候我们需要将数字进行左移,这就是左规,但是当小数点前的数超过了2,那么我们就需要将数字进行右移,这就是左规。

        尾数在国际标准IEEE中就是用原码来表示的,但是有些题目可能会给出尾数用补码来表示的要求,那么我们就需要了解,原码表示的尾数和补码表示的尾数分别有什么要求:前面我们了解了规格化的问题,那么二进制世界中,小数点前的数在规格化后只能是1,所以为了使浮点数能够表示的数值范围更大,我们可以将这个1从二进制浮点数中隐藏起来,也就是像基数一样,默认这里有一个1,所以尾数的第一个数字就是小数点后的第一个数字。那么在原码中这个小数点前的数必须是1,然后被隐含起来,而在补码中,如果这个补码是正数,那么就和原码一样,但是如果这个数是一个负数,那么按照补码转原码取反的规则,这个隐藏起来的数值位最高位就必须是0。

        6.2浮点数的运算

        当我们在进行浮点数的运算过程中,一般情况下题目给出的两个浮点数的阶数都不同,所以在浮点数的运算中一般分为4个步骤:对阶->尾数求和->规格化->舍入->溢出判断。

        对阶:对阶其实有点像是规格化的逆运算,固定要求是将阶数小的向阶数大的对齐,那么就会发生一个问题,阶数小的向阶数大的对齐过程通常会使数字右移,但是尾数的bit位有限,所以可能会导致尾数丢失精度。

        尾数求和:尾数求和直接相加即可,在这个过程中,可能会发生溢出,也就是两个尾数在加和的过程中,导致了小数点前面的数大于1了,发生了尾数的溢出。

        规格化:规格化正是为了解决尾数求和发生尾数的溢出问题,将加和后得到的数进行规格化后,重新使小数点前的数为1即可,但是规格化就代表着右移同样可能会丢失精度。

        舍入:舍入一般有两种方式,第一种:0直接舍弃,1不能舍弃需要加到1的前一位上去,这种方式可能导致尾数的左溢出需要右规;第二种:不管是0还是1都直接舍弃,但是最后一位恒置为1,这种方式虽然会丧失精度,但是不会发生尾数的溢出。

        溢出判断:前面的几个步骤我们说的都是尾数的溢出,尾数的溢出可以通过规格化来进行修补,虽然会丧失一定精度,但是总归来说最终结果是正确的,但是阶码的溢出会导致结果的错误,所以这里的溢出判断指的是指数的溢出,规格化的过程会使指数增大或者减小,我们要通过题中给出的浮点数的阶码具体占据多少bit位来相应的判断次数阶码是否发生溢出。

        

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值