本文参考于《2021年计算机组成原理考研复习指导》(王道考研),《计算机组成原理》
在计算机中,小数和整数不一样,整数可以连续表示,但小数是离散的(二进制表示小数时只能用1/(2n)的和的任意组合表示),所以并不是每一个十进制小数都可以准确地用二进制表示。但任意一个二进制小数都可用十进制小数表示。
真值0的原码表示有正0和负0两种形式。[+0]原=0 0000和[-0]原=1 0000
对于纯小数的补码,若字长为n+1,补码的表示范围为-1~1-2-n(比原码多表示-1)
对于纯整数的补码,若字长为n+1,补码的表示范围为-2n~2n-1(比原码多表示-2n)
真值0的补码表示是唯一的。即[+0]补=[-0]补=0.0000,由定义得[-1]补=10.0000-1.0000=1.0000,对于小数,补码比原码多表示一个-1(这是因为补码中的0只有1种表示形式,故它能比原码多表示一个-1)。对于整数,[-8]补=10000-1000=1000,补码比原码多表示一个-2n(因为补码没有负0,故诸如10000000这样的数表示的是8位补码的最小数-128)
模4补码双符号位00表示正,11表示负,用在完成算术运算的ALU部件中
(4)移码表示法
能直接从移码形式判断其真值的大小,又因为检验移码的特殊值较容易,因此移码常用来表示浮点数的阶码。它只能表示整数。
移码就是在真值X上加上一个常数(偏置值),通常这个常数取2^n,相当于X在数轴上向正方向偏移了若干单位。
一个真值的移码和补码仅差一个符号位,[x]补的符号位取反即得[x]移(1正0负,与其他机器数符号位取值正好相反),反之亦然
移码全0时,对应真值的最小值-2^n;移码全1时,对应真值的最大值
2 ^ n-1
移码保持了数据原有的大小顺序,移码大真值就大,移码小真值就小
二进制表示的机器数在相对于小数点作n位左移或右移时,其实质就是该数乘以或除以2^n
移位运算根据操作对象的不同分为算术移位和逻辑移位。有符号数的移位称为算术移位,逻辑移位的操作对象是逻辑代码,可视为无符号数
(1)算术移位
算术移位的对象是有符号数,在移位过程中符号位保持不变
对于正数,由于[x]原=[x]补=[x]反=真值,因此移位后出现的空位均用0填充。对于负数,由于原码、补码、反码的表示形式不同,因此当机器数移位时,对其空位的填补规则也不同。(不论是正数还是负数,移位后其符号位均不变,且移位后都相当于对真值补0,根据补码、反码的特性,在负数时填充代码有区别)
对于原码,左移一位若不产生溢出,相当于乘以2,右移一位,若不考虑因移出而舍去的末位尾数,相当于除以2。
不同机器数算术移位后的空位填补规则为:
负数的原码数值部分与真值相同,在移位时只要使符号位不变,其空位均填0;负数的反码各位除符号位外与负数的原码正好相反,因此移位后所填的代码应与原码相反,即全部填1。
在由原码得补码的过程中(由高位到低位找到最后一个1后,后面的照抄),当对其由低位向高位找到第一个1时,在此1左边的各位均与对应的反码相同,在此1右边的各位(包括1在内)均与对应的原码相同。因此负数的补码左移时,因空位出现在低位,填补的代码与原码相同,即填0;右移时空位出现在高位,填补的代码与反码相同,即填1。
(2)逻辑移位
逻辑移位将操作数视为无符号数,逻辑左移时,高位移丢,低位填0,右移时,低位移丢,高位填0;逻辑移位不管是左移还是右移,都填0;
(3)循环移位
循环移位分为带进位标志位CF的循环移位(大循环)和不带进位标志位的循环移位(小循环)
(a)不带进位的循环右移:操作数循环右移位,空出的高位部分由移出的低位部分来填充。同时,CF中只保存最后一次移出的那一位的内容
(b)带进位的循环右移:操作数连同CF位循环右移,用原CF的值填补空出的位,移出的位再进入CF。
(c)不带进位的循环左移:操作数循环左移位,空出的低位部分由移出的高位部分来填充。同时,CF中只保存最后一次移出的那一位的内容
(d)带进位的循环左移:操作数连同CF位循环左移,用原CF的值填补空出的位,移出的位再进入CF。
补码运算符号位与数值位按同样规则一起参与运算,符号位运算产生的进位要丢掉,结果的符号位由运算得出
在计算机算术运算中,有时必须把采用给定位数表示的数转换成具有不同位数的某种表示形式。例如,某个程序要将一个8位数与一个32位数相加,要想得到正确的结果,在将8位数与32位数相加之前,必须把8位数转换成32位数形式,这称为符号拓展。
正数的符号拓展即原有形式的符号位移动到新形式的符号位上,新表示形式的所有附加位都用0填充
负数的符号拓展方法则根据机器数的不同而不同。原码表示负数的符号拓展方法与正数相同,此时符号位为1。补码表示负数的符号拓展方法:原有形式的符号位移动到新形式的符号位上,新表示形式的所有附加位都用1(对于整数)或0(对于小数)进行填充。反码表示负数的符号拓展方法:原有形式的符号位移动到新形式的符号位上,新表示形式的所有附加位都用1进行填充。
称大于机器所能表示的最大正数为上溢,小于机器所能表示的最小负数为下溢。
仅当两个符号相同的数相加或两个符号相异的数相减才有可能产生溢出,如两个正数相加结果符号位为1,一个负数减一个正数,结果符号位为0
补码定点数加减运算溢出判断的方法有3种:
(1)采用一位符号位
由于减法运算在机器中是用加法器实现的,因此无论是加法还是减法,只要参加操作的两个数符号相同,结果又与原操作数不同,则表示结果溢出
(2)采用双符号位
双符号位法也称模4补码。运算结果的两个符号位Ss1Ss2相同,表示未溢出;不同表示溢出,此时最高位符号位代表真正的符号
符号位的各种情况:
Ss1Ss2=00;结果为正,未溢出
Ss1Ss2=01;结果为正,溢出
Ss1Ss2=10;结果为负,溢出
Ss1Ss2=11;结果为负,未溢出
采用双符号位时,寄存器或主存中的操作数只需保存1位符号位即可。因为任何正确的数,两个符号位的值总是相同的,而双符号位在加法器中又是必要的,故在相加时,寄存器中1位符号的值要送到加法器的两个符号位的输入端
int main()
{
short x=-4321;
unsigned short y=(unsigned short)x;
printf("x=%d,y=%u\n",x,y);
}
上述代码会输出x=-4321,y=61215
x=(1110 1111 0001 1111)2,y=(1110 1111 0001 1111)2
x为补码表示,y为无符号的二进制真值,被解释为61215。将short int 强制转换成unsigned short 只改变数值,而两个变量对应的每一位都是一样的。强制类型转换的结果保持位值不变,仅改变了解释这些位的方式。
int main()
{
unsigned short x=65535;
short y=(short)x;
printf("x=%u,y=%d\n",x,y);
}
输出x=65535,y=-1
int main()
{
int x=165537,u=-34991; //int型占用4B
short y=(short)x,v=(short)u; //short型占用2B
printf("x=%d,y=%d\n",x,y);
printf("u=%d,v=%d\n",u,v);
}
输出x=165537,y=-31071,u=-34991,v=30545
其中x、y、u、v的16进制表示分别为0x000286a1、0x86a1、0xffff7751、0x7751。当大字长变量向小字长变量强制类型转换时,系统把多余的高位字长部分直接截断,低位直接赋值。
int main()
{
short x=-4321;
int y=x;
unsigned short u=(unsigned short)x;
unsigned int v=u;
printf("x=%d,y=%d\n",x,y);
printf("u=%u,v=%u\n",u,v);
}
输出x=-4321,y=-4321,u=61215,v=61215
x、y、u、v的16进制表示分别是0xef1f、0xffffef1f、0xef1f、0x0000ef1f;
短字长整数到长字长整数的转换,不仅要使对应的位值相等,高位部分还会扩展为原数字的符号位。(短字长到长字长的转换,在位值相等的条件下还要补充高位的符号位,转换后所表示的数值与原数值一样)
char类型为8位ASCII码整数,其转换为int时,在高位部分补0即可
在存储数据时,数据从低位到高位可按从左至右排列,也可按从右到左排列。通常用最低有效字节(LSB)和最高有效字节(MSB)来分别表示数的低位和高位。例如,在32位计算机中,一个int型变量i的机器数为01 23 45 67H,其最高有效字节MSB=01H,最低有效字节LSB=67H。
现代计算机基本上都采用字节编址,即每个地址编号中存放1字节。不同类型的数据占用的字节数不同,int和float型数据占4B,double型数据占8B,而程序中对每个数据只给定一个地址。
多字节数据都存放在连续的字节序列中,根据数据中各字节在连续字节序列中的排列顺序不同,可采用两种排列方式:大端方式(big endian)和小端方式(little endian)
大端方式按从最高有效字节到最低有效字节的顺序存储数据,即最高有效字节存放在前面;小端方式按从最低有效字节到最高有效字节的顺序存储数据,即最低有效字节存放在前面。
例如,反汇编得到:4004d3: 01 05 64 94 04 08 add %eax,0x8049464
其中4004d3是16进制表示的地址,01 05 64 94 04 08是指令的机器代码,add %eax,0x8049464是指令的汇编形式,该指令的第二个操作数是一个立即数0x8049464。执行指令时,从指令代码的后4字节中取出该立即数,可知采用的是小端方式存储。(在小端方式存储的机器代码中,字节是按相反顺序显示的)
假设存储字长为32位,可按字节、半字和字寻址。对于机器字长为32位的计算机,数据以边界对齐方式存放,半字地址一定是2的整数倍,字地址一定是4的整数倍,这样无论所取的数据是字节、半字还是字,均可一次性访存取出。所存储的数据不满足上述条件时,填充空白字节。使用这种方式浪费了一些存储空间,提高了取指令和数据的速度(空间换时间)。
数据不按边界对齐方式存储时,可充分利用存储空间,但半字长或字长的指令可能会存储在两个存储字中,此时需2次访存,且对高低字节的位置进行调整、连接之后才能得到所要的指令或数据。
浮点数即小数点的位置能浮动的数,通常,浮点数表示为N=rE x M,r是浮点数阶码的底(隐含),与尾数的基数相同,通常r=2。E和M都是有符号的定点数,E称为阶码,M称为尾数。浮点数由阶码和尾数两部分组成。
阶码是整数,阶符Jf和阶码的位数m共同反映浮点数的表示范围及小数点的实际位置;数符Sf代表浮点数的符号;尾数的位数n反映浮点数的精度;基数r越大,可表示的浮点数范围越大,所表示的数的个数越多,但浮点数精度下降
为提高运算的精度,需充分利用尾数的有效数位,通常采取浮点数规格化形式,即规定尾数的最高数位必须是一个有效值。非规格化浮点数需要进行规格化操作才能变成规格化浮点数。规格化操作即指通过调整一个非规格化浮点数的尾数和阶码的大小,使非0的浮点数在尾数的最高数位上保证是一个有效值。
左规:将尾数算术左移一位、阶码减1(基数为2时)的方法称为左规,左规可能要进行多次。
右规:当浮点数运算的结果尾数出现溢出(双符号位为01或10)时,将尾数算术右移一位,阶码加1(基数为2时)的方法称为右规。需要右规时,只需进行一次。
规格化表示的尾数形式如下:
当浮点数尾数的基数为2时,原码规格化数的尾数最高位一定是1,补码规格化数的尾数最高位一定与尾数符号位相反。基数不同,浮点数的规格化形式也不同。当基数为4时,原码规格化形式的尾数最高2位不全为0,规格化时,尾数左/右移2位,阶码减/加1;当基数为8时,原码规格化形式的尾数最高3位不全为0,规格化时,尾数左/右移3位,阶码减/加1
按照IEEE754标准的浮点数格式如图:
IEEE754标准规定常用的浮点数格式有短浮点数(单精度、float型)、长浮点数(双精度、double型)、临时浮点数
IEEE754标准的浮点数(除临时浮点数外),是尾数用采取隐藏位策略的原码表示,且阶码用移码表示的浮点数。
以短浮点数为例,最高位为数符位,其后是8位阶码,以2为底,用移码表示,阶码的偏置值为28-1-1=127;其后23位是原码表示的尾数数值位。对于规格化的2进制浮点数,数值的最高位总是1,为了能使尾数多表示一位有效位,将这个1隐含,因此尾数数值实际上是24位。隐含的1是一位整数。在浮点格式中表示的23位尾数是纯小数。例如,(12)10=(1100)2,规格化后结果为1.1x23,其中整数部分的1将不存储在23位尾数内。(短浮点数与长浮点数都采用隐含尾数最高数位的方法,临时浮点数又称扩展精度浮点数,无隐含位)
为何要用移码表示阶码:a.移码比较大小更方便;b.检验移码的特殊值时较容易
移码即真值+偏置值。偏置值为127,而非128的原因是:指数可以为正数也可以为负数,为将负数转变为正数(为了不在阶码中引入阶符),实际的指数值要加上偏置值。
-127~+127加上127即得到 0 ~ +254,然而阶码值E的范围为1~254,因为阶码为0时表示指数为负无穷大,整个数无穷接近于0,可将0用于表示0(尾数也为0时)或非规格化数(尾数不为0时)。为什么不加128呢?因为255为8位全1,也有特殊用途,当阶码值为255时(指数为正无穷),若尾数部分为0,则表示无穷大;若尾数部分不为0,则认为这是一个“非数值”(浮点数运算错误)
阶码是以移码形式存储的。对于短浮点数,偏置值为127;对于长浮点数,偏置值为1023。存储浮点数阶码部分之前,偏置值要先加到阶码真值上。例如1.1x23,阶码值为3,在短浮点数中,移码表示的阶码为130(82H);在长浮点数中,阶码为1026(402H)
IEEE754标准中,规格化的短浮点数真值为:(-1)s x 1.M x 2 E-127;规格化长浮点数真值为:(-1)s x 1.M x 2 E-1023
短浮点数E的取值为1~ 254(8位表示),M为23位,共32位;长浮点数E的取值为1~2046(11位表示),M为52位,共64位。IEEE754标准浮点数的范围为:
定点、浮点表示的区别
(1)数值的表示范围
若定点数和浮点数的字长相同,则浮点表示法所能表示的数值范围将远远大于定点表示法
(2)精度
精度指一个数所含有效数值位的位数。对于字长相同的定点数和浮点数来说,浮点数虽然扩大了数的表示范围,但精度降低了(要用字长的一部分表示阶码,尾数部位的有效位数减少)
(3)数的运算
浮点数包括阶码和尾数两部分,运算时不仅要做尾数的运算,还要做阶码的运算,而且运算结果要求规格化
(4)溢出问题
在定点运算中,当运算结果超出数的表示范围时,发生溢出;浮点运算中,运算结果超出尾数表示范围时不一定溢出,只有规格化后阶码超出所能表示的范围时,才发生溢出
对于位数相同的定点数与浮点数,可表示的浮点数个数比定点数个数多吗?
否,可表示的数据个数取决于编码所采用的位数。编码位数一定,编码出来的数据个数是一定的。n位编码只能表示2^n个数,所以对于相同位数的定点数与浮点数来说,可表示的数据个数应该一样多(有时可能由于一个值有2个或多个编码对应,编码个数会有少量差异)
2.3.2 浮点数的加减运算
阶码运算和尾数运算分开进行。浮点数的加减运算一律采用补码。
对阶的目的是使两个操作数的小数点位置对齐,即使得两个数的阶码相等。先求阶差,然后以小阶向大阶看齐的原则,将阶码小的尾数右移一位(基数为2),阶加1,直到两个数的阶码相等为止。尾数右移时,舍弃掉有效位会产生误差,影响精度
将对阶后的尾数按定点数加减运算规则计算
以双符号位为例,当尾数大于0时,其补码规格化形式为:
[S]补=00.1xx…x
当尾数小于0时,其补码规格化形式为:
[S]补=11.0xx…x
当尾数的最高数值位与符号位不同时,即为规格化形式。规格化分为左规和右规
(1)左规
当尾数出现00.0xx…x或11.1xx…x时,需左规,尾数左移一位,阶码减1,直到尾数为00.1xx…x或11.0xx…x
(2)右规
当尾数求和结果溢出(如尾数为10.xx…x或01.xx…x)时,需右规,尾数右移1位,阶码加1
在对阶和右规的过程中(进行了右移),可能会将尾数低位丢失,引起误差。常见的舍入方法有:0舍1入法和恒置1法
0舍1入法:类似于4舍5入,尾数右移时,被移去的最高数值位为0,则舍去;被移去的最高数值位为1,则在尾数的末位加1。这样做可能会使尾数再一次溢出,此时需再做一次右规。
恒置1法:尾数右移时,都使右移后的尾数末位恒置1,此方法有使尾数变大和变小(尾数为负时)两种可能。
当尾数之和(差)出现10.xx…x或01.xx…x时,并不表示溢出,需将此数右规后,根据阶码判断结果是否溢出
浮点数溢出与否是由阶码的符号决定的。以双符号位补码为例,当阶码的符号位出现01时,即阶码大于最大阶码,表示上溢,进入中断处理;当阶码的符号位出现10时, 阶码小于最小阶码,表示下溢,按机器零处理。
C语言中的float和double类型分别对应于IEEE754单精度浮点数和双精度浮点数。long double类型对应于扩展双精度浮点数,但其长度和格式随编译器和处理器类型的不同而不同。在C程序中等式的赋值和判断中会出现强制类型转换,以char->int->long->double
和float->double
最为常见,从前到后范围和精度都从小到大,转换过程中没有损失。
并行加法器的最长运算时间主要是由进位信号的传递时间决定的,而每个全加器本身的求和延迟只是次要因素。
提高并行加法器速度的关键是尽量加快进位产生和传递的速度
并行加法器中的每个全加器都有一个从低位送来的进位输入和一个传送给高位的进位输出。通常将传递进位信号的逻辑线路连接起来构成的进位网络称为进位链。