diff --git a/Computer-Organization/1-data-representation-and-operation.md b/Computer-Organization/1-data-representation-and-operation.md index 0bbdde4..d6ad1b1 100644 --- a/Computer-Organization/1-data-representation-and-operation.md +++ b/Computer-Organization/1-data-representation-and-operation.md @@ -636,16 +636,148 @@ long double|临时浮点数|1|15|64|80|3FFFH|16383 ### 浮点数运算 +#### 科学计数法加减运算 + +1. 对阶:阶数小的向阶数更大的对齐。 + + 因为计算机内部,尾数是定点小数,小数点位置不会变,改变的只是数据的相对位置。 + + 若是阶数大的向阶数小的对齐,则阶数大的尾数值会变大,需要对尾数进行算术左移,若内存不够大很可能会引起最高有效位丢失。 + + 若是阶数小的向阶数大的对齐,则阶数小的尾数值会变小,需要对尾数进行算术右移,若内存不够大很可能会引起最后几位丢失,即精度下降,影响较小。 +2. 尾数加减:阶数不变,对尾数进行相加减。 +3. 规格化,将数据变为整数部分为0到9的数据: + + 当尾数加减结果的第一位为0时需要左规,直到第一位不为0。 + + 当结果的整数部分大于等于10需要右规,直到整数部分只有一位。 +4. 舍入:计算机中由于尾数的比特位有限,所以需要舍弃尾数的低位。舍入方法有: + + 直接去除。 + + 非0进1。 + + 四舍五入。 +5. 判溢出:若运算后阶码超过规定范围则溢出。尾数的溢出未必会导致整体溢出,可以通过第三四步来修补,但是阶码溢出一定会整体溢出。 + +**例题** 计算$9.85211\times10^{12}+9.96007\times10^{10}$的值并保留四舍五入六位有效尾数。 + +第一步进行对阶,变成$9.85211\times10^{12}+0.0996007\times10^{12}$。 + +第二步进行相加得到$9.9517107\times10^{12}$。 + +第三步由于整数部分为9,所以不需要规格化。 + +第四步因为只能保留六位有效尾数,所以保留9.95171,因为后一位是0,所以不进位为最后答案。 + +第五步因为阶码无论是10或12都是两位,所以肯定没有溢出。 + #### 浮点数加减运算 -1. 对阶。 -2. 尾数加减。 -3. 规格化。 -4. 舍入。 -5. 判溢出。 +浮点数的加减基本上与科学计数法的加减一致。基本上浮点数的运算不可能使用IEEE 754的标准,因为位数太长不好计算。 + +**例题** 已知十进制数$X=-5/256$,$Y=+59/1024$,按机器补码浮点运算规则计算$X-Y$,结果用二进制表示,浮点数格式如下︰阶符取2位,阶码取3位,数符取2位,尾数取9位。 + +首先要将十进制的真值转换为二进制原码。因为不是IEEE 754标准,所以小数不用转换为1.xxx的格式,转成0.xxx的格式就可以了:$5D=101B$,$1/256=2^{-8}$,所以$X=-101\times2^{-8}=-0.101\times2^{-5}=-0.101\times2^{-101B}$。然后$59D=11 1011B$,$1/1024=2^{-10}$,从而$Y=+11 1011\times2^{-10}=0.111011\times2^{-4}=0.111011\times2^{-100B}$。 + +X的阶码为-101,转换为补码表示为1011,由于使用双符号位,所以变成11011;X的尾数为-0.101,补码表示为1.011,由于双符号位,所以为11.011,尾数取9位,所以拓展为11.0110 0000 0。所以X就是11011,11.011000000。 + +同理Y的阶码为-100,转换为补码表示为1100,双符号位所以变为11100;Y的尾数为0.111011,补码表示为0.111011,使用使用双符号位为00.111011,拓展为9位得到00.111011000.所以Y就是11100,00.111011000。 + +然后是阶数对齐,小阶向大阶靠拢。首先求阶差:11011-11100=11011+00100=11111=11,001=-1D。所以X的阶数比Y的阶数小一。所以对X算术右移,负数高位补1,阶码加一,从而由11011,11.011000000变为了11100,11.101100000。即变为了$-0.0101×2^{-100B}$。 + +然后是尾数加减,所以Y的尾数需要变成其负值的补码,方法是对尾数包括符号位全部取反,然后末尾加1,所以-Y=11100,11.000101000。进行相加得到10.110001000。这时候代表出现上溢出(用二进制计算尾数变成-1.001111,由于定点小数无法表示绝对值大于等于1的数,所以上溢)。 + +第三步规格化,因为使用双符号位,所以正好能补救计算结果的上溢,可以通过右规的方式规格化。所以将10.110001000算术右移,将符号位的0以及后面的数据全部右移一位,符号位补成11,得到11.011000100。然后阶码由于右移而加一,最后为11101,11.011000100。 + +第四步舍入,由于第三步右移抛弃的是0,所以没有丧失精度,就不需要舍入。 + +第五步判溢出,11101加1后等于11110,符号位都是11,所以没有溢出。 + +最后真值为11101,11.011000100。即$2^{-3}\times(-0.1001111)_2$。 + +舍入的方法: + ++ “0”舍“1”入法:类似于十进制数运算中的“四舍五入”法,即在尾数右移时,被移去的最高数值位为0,则舍去;被移去的最高数值位为1,则在尾数的末位加1。这样做可能会使尾数又溢出,此时需再做一次右规。 ++ 恒置“1”法:尾数右移时,不论丢掉的最高数值位是“1”还是“O”,都使右移后的尾数末位恒置“1”。这种方法同样有使尾数变大和变小的两种可能。 + +假如加减的结果为11100,10.110001011,因为尾数符号位为10代表溢出了,所以需要规格化。 + +使用0舍1入法,将尾数整体右移,并将符号位的低位移到尾数高位,阶码加移位数,符号位修改,从而变成了11101,11.011000101,溢出1,最高位为1,使用0舍1入时需要加1,从而变成11101,11.011000110。(假如尾数为全1,则加1后又会高位溢出,还需要一次右规)。恒置1法的答案是一样的。 #### 浮点数强制类型转换 +类型|16位机器|32位机器|64位机器 +:--:|:------:|:------:|:------: +char|8|8|8 +short|16|16|16 +int|16|32|32 +long|32|32|64 +long long|64|64|64 +float|16|32|32 +double|64|64|64 + +无损转换: + ++ char→int→long→double。 ++ float→double。 + +由于定点数和浮点数不同,浮点数使用阶码+尾数的存储方式存储,所以定点数数值精度看位长就可以了,而浮点数数值精度看尾数长度,按IEEE 754标准有一个隐含的高位1,所以double尾数长度为53位。对于32位机器long是32位,所以转换到double的53位没有损失,而对于64位机器long是64位,double还是53位,这时候转换就会产生损失了。 + +int与float转换 + ++ int:表示整数,范围$[-2^{31},2^{31}-1]$,有效数字32位。 ++ float:表示整数及小数,范围$\pm[2^{-126},2^{127}×(2-2^{-23})]$,有效数字23+1=24位。 ++ int→float:可能损失精度。 ++ float→>int:可能溢出及损失精度。 ## 算术逻辑单元 +即运算器中的ALU。 + +### 原理 + +#### ALU结构 + ++ 输入信号有一个操作数的输入口,输出信号有一个运算结果输出口,旁边还有一个控制单元CU发出的控制信号接口,会输入指令译码。 ++ 机器字长就是指ALU可以同时处理多长的数据。为了保存结果,寄存器的字长与机器字长相等。 + +#### 逻辑运算 + ++ 与可以用*表示,如A\*B。 ++ 或可以用+表示,如A+B。 ++ 优先级上类比乘法加法,与优先于或。 ++ 具有类似的分配律结合律。 ++ 与非:先与后非。 ++ 或非:先或后非。 ++ 异或:相异为1,相同为0。 ++ 同或:相同为0,相异为1。 + +### 加法器实现 + +#### 一位全加器 + ++ 一位全加器FA中,令被加数为$A$,被加数从低到高的位数为$A_i$,令加数为$B$,被加数从低到高的位数为$B_i$,令来自低位$i-1$的进位为$C_{i-1}$,本位的和为$S_i$。$S_i=A_i+B_i+C_{i-1}$。 ++ 输入$A_i$、$B_i$、$C_{i-1}$,输出$S_i$、$C_i$。 ++ 输入中有奇数个1时为1(异或):$S_i=A_i\oplus B_i\oplus C_{i-1}$。 ++ 输入中至少两个1才会高位进1,一种情况是两个本位都是1,另一种情况是有一个是1且来自低位的进位是1:$C_i=A_iB_i+(A_i\oplus B_i)C_{i-1}$。 + +#### 串行加法器 + ++ 基于一个一位全加器,一位一位串行进入加法器运算。对比一位全加器,需要保存一个进位触发器用来保存进位位,进位触发器初始化值为0。 ++ 如果操作数长n位加法就要务n次进行,每次产生一位和,并且串行逐位地送回奇存器。 + +#### 串行进位并行加法器 + ++ 把n个全加器串接起来,就可进行两个n位数的相加。前一个全加器的进位输出将作为下一个全加器的输入。 ++ 串行进位又称为行波进位,每一级进位直接依赖于前一级的进位,即进位信号是逐级形成的。所以不可能比单纯的串行全加器快很多。 + +#### 并行进位并行加法器 + ++ 由于$S_i=A_i\oplus B_i\oplus C_{i-1}$,所以计算的重点是计算$C_i$,而$C_i=A_iB_i+(A_i\oplus B_i)C_{i-1}$,通过递归可以不断展开:$C_i=A_iB_i+(A_i\oplus B_i)(A_{i-1}B_{i-1}+(A_{i-1}\oplus B_{i-1})C_{i-2})$一直可以递归到$C_0$,而$A_i$、$B_i$、$C_0$都是一开始可以知道的,所以第$i$位向更高位的进位$C_i$可根据被加数、加数的第1到$i$位,再结合$C_0$即可确定。 ++ 将$G_i=A_iB_i$,$P_i=A_i\oplus B_i$,所以式子得到化简:$C_i=A_iB_i+(A_i\oplus B_i)C_{i-1}=G_i+P_iC_{i-1}$。 ++ 所以$C_1=G_1+P_1C_0$,$C_2=C_2+P_2C_1=G_2+P_2G_1+P_2P_1C_0$,$C_3=G_3+P_3C_2=G_3+P_3G_2+P_3P_2G_1+P_3P_2P_1C_0$……所以一直到最后面每个$C_i$都可以用对应的$G_i$和$P_i$计算,这时候$C_i$可以不用递归来计算,可以用一开始就知道的$P_i$、$G_i$和$C_0$计算得到,从而这时候每一位的计算都可以一开始就同时进行了而不用依赖前面的计算。 ++ 各级进位信号同时形成,又称为先行进位、同时进位。 ++ $G_i$称为进位产生函数,因为$G_i$是通过$A_i$和$B_i$相与,只有同时为1才会产生1。 ++ $P_i$称为进位传递函数,$P_i$是通过$A_i$和$B_i$异或,实际上$P_i$为1时,只有此时$C_{i-1}$才能为1,否则为0。 + +#### 单级先行进位并行加法器 + ++ 称为组内并行、组间串行进位方式。 ++ 由于逻辑表达式越长就代表电路实现越复杂,所以一般会最多用4个FA和一些新的线路、运算逻辑组成一个运算单元进行串联进位计算。 ++ 组内的信息可以并行同时得到,但是组件信息需要串行进位。这时虽然实现简单,但是效率对比并行进位并行加法器还是下降了。 ++ 为了解决这个问题按照并行进位并行加法器的思路继续对每一组的数据进行计算。 ++ 令$G_1^*=G_4+P_4G_3+P_4P_3G_2+P_4P_3P_2G_1$,$P_1^*=P_4P_3P_2P_1$。 +