文章目录
3.4整数乘除运算
十进制加法运算举例
- 问题:本位和在什么范围内需+6校正
结果<=9时。不需校正:大于9或有进位时。需+6校正 - 最高位有进位时,发生溢出
大于9:1010.1011…1111
有进位:10000.10001,10010和10011 - 当结果在10-19之间时需校正
[最大可能:2x9+1=19)即:
1x1x或11xx或有进位(C4*=1)
所以。校正逻辑表达式:C=C4*+S3S1+S3S2
一位十进制加法器
十进制减法运算
- 方法:
“加补码”:N1-N2=N1+10nN2(mod10n) - 十进制数的补码求法:
·每位求反,未位加1" - 一位十进制数(NBCD码)求反的方法,有:
对各二进位求反。再“+10“
先“+6“,再各位求反
直接用求反电路
MIPS中整数的乘、除运算处理
- 指令: mult ,multu;div ,divu
- MIPS中有一对32位寄存器Hi & Lo (相当于Q乘商寄存器)
- 乘法和除法运算的硬件相同:
- 仅需做加、减和64位寄存器的左/右移位
- Hi和Lo结合起来实现64位寄存器
- 乘法: Hi中存放高32位积, Lo中存放低32位积
- 除法:Hi中存放remainder, Lo中存放 quotient
- mflo/mfhi指令用来把Lo/Hi中的32位数据取到通用寄存器
- 两种乘法指令都忽略overflow, 而由软件自行处理溢出
- 软件通过mfhi指令取出Hi寄存器来判断是否溢出
- 溢出判断规则: Hi中为以下数值时不溢出,否则溢出
- 无符号数乘指令(multu)时:全0
- 带符号数乘(mult )时: Lo中的符号
- MIPS指令不处理异常,由系统软件自行处理
整数的乘运算
-
结论:
- 假定两个n位无符号整数xu和yu对应的机器数为Xu和Yu,pu=xu×yu,pu为n位无符号整数且对应的机器数为Pu;
- 两个n位带符号整数xs和ys对应的机器数为Xs和Ys,ps=xs×ys,ps为n位带符号整数且对应的机器数为Ps。
- 若Xu=Xs且Yu=Ys,则Pu=Ps。
-
X*Y的高n位可以用来判断溢出,规则如下:
- 无符号:若高n位全0,则不溢出,否则溢出
- 带符号:若高n位全0或全1且等于低n位的最高位,则不溢出。
-
在计算机内部,一定有x2 ≥ 0吗?
- 若x是带符号整数,则不一定!
例如,当 n=4 时, 52=-7<0 ! - 如x是浮点数,则一定!
- 高级语言程序如何判断z是正确值?
- 乘积的高n位为全0,则不溢出
- 若x、y和z都改成unsigned类型,则判断方式为
当 !x || z/x==y 为真时
- 编译器如何判断?
当 -2n-1 ≤ x*y < 2n-1(不溢出)时
即:乘积的高n位为全0或全1,并等于低n位的最高位!
即:乘积的高n+1位为全0或全1
- 若x是带符号整数,则不一定!
-
硬件可保留2n位乘积,故有些指令的乘积为2n位,可供软件使用
-
如果程序不采用防止溢出的措施,且编译器也不生成用于溢出处理的代码,就会发生一些由于整数溢出而带来的问题。
-
指令:分无符号数乘指令、带符号整数乘指令
-
乘法指令的操作数长度为n, 而乘积长度为2n,例如:
- IA-32中,若指令只给出一个操作数SRC,则另一个源操作数隐含在累加器AL/AX/EAX中,将SRC和累加器内容相乘,结果存放在AX(16位时)或DX-AX(32位时)或EDX-EAX(64位时)中。
- 在MIPS处理器中,mult会将两个32位带符号整数相乘,得到的64位乘积置于两个32位内部寄存器Hi和Lo中,因此,可以根据Hi寄存器中的每一位是否等于Lo寄存器中的第一位来进行溢出判断。
-
乘法指令可生成溢出标志,编译器也可使用2n位乘积来判断是否溢出!
乘运算举例
- 在字长为32位的计算机上,某C函数原型声明为:
int imul_overflow(int x, int y);
该函数用于对两个int型变量x和y的乘积(也是int类型)判断是否溢出,若溢出则返回非0,否则返回0。请完成下列任务或回答下列问题。-
(1)两个n位无符号数相乘、两个n位带符号整数相乘的溢出判断规则各是
什么?(编译器如何判断溢出?) -
(2)已知入口参数x、y所在存储单元的地址为R[ebp]+8、R[ebp]+12,
返回值在EAX中,写出实现imul_overflow函数功能的AT&T汇编指
令序列,并给出注解。(编译器中判断溢出的代码) -
(3)使用64位整型(long long)变量来编写imul_overflow函数的C代码
或描述实现思想。
(1)无符号整数相乘:若乘积的高n位为非0,则溢出。
带符号整数相乘:若乘积高n位的每一位都相同,且都等于乘积低n
位的符号,则不溢出,否则溢出。
(2)实现该功能的汇编指令序列不唯一。
(例如,可利用imull指令生成的OF标志进行判断)
某实现方案下的IA-32汇编指令序列如下:
movl 8(%ebp), %eax /* R[eax]=x
movl 12(%ebp), %edx /* R[edx]=y
imull %edx /* R[edx]:R[eax]=x*y
sarl $31, %eax /* R[eax]=R[eax]>>31, 符号扩充31位
xorl %edx, %eax /*按位异或,若结果为0,表示不溢出
(3)将x*y的结果保存在long long型变量中,得到64位乘积,然后把64位乘积强制转换为32位,再符号扩展成64位,和原来真正的64位乘积相比,若不相等则溢出。int imul_overflow(int x, int y) { long long prod_64= (long long) x\*y; return prod_64 != (int) prod_64; }
-
关于乘运算的几个问题
- 假定CPU中没有乘法器,只有ALU、移位器以及与、或、非等逻辑电路,则其指令系统能提供乘法指令吗?
- 假定ISA中不包含乘法指令,但包含加、减、移位以及与、或、非三种逻辑运算指令,则基于该ISA的系统能执行以下程序吗?
int imul(int x, int y) {
return x*y;
} - 以下两个函数的机器级代码相同吗? 返回的结果一定相同吗?什么情况下相同?返回的结果一定正确吗?
int imul(int x, int y) {
return xy;
}
unsigned imul(unsigned x, unsigned y) {
return xy;
} - 上述程序执行时间比较
① 无乘法指令 > ②有用ALU实现乘法指令 > ③具有用乘法器实现乘法指令 - ①:用循环程序实现
②:直接用较慢指令实现
③:直接用较快指令实现
3.4.2 变量与常数之间的除运算
- 不能整除时,采用朝零舍入,即截断方式
- 无符号数、带符号正整数(地板):移出的低位直接丢弃
- 带符号负整数(天板):加偏移量(2k-1),然后再右移k 位 ,低位截断(这里K 是右移位数)
- 举例:
无符号数 14/4=3:0000 1110>>2=0000 0011
带符号负整数 -14/4=-3
若直接截断,则 1111 0010 >>2=1111 1100=-4≠-3
应先纠偏,再右移: k=2, 故(-14+22-1)/4=-3
即: 1111 0010+0000 0011=1111 0101
1111 0101>>2=1111 1101=-3 - -9/2: 10111+00001=11000
11000>>1=11100=-4
变量与常数之间的除运算—举例
- 假设x为一个int型变量,请给出一个用来计算x/32的值的函数div32。要求不能使用除法、乘法、模运算、比较运算、循环语句和条件语句,可以使用右移、加法以及任何按位运算。
- 解:若x为正数,则将x右移k位得到商;若x为负数,则x需要加一个偏移量(2k-1)后再右移k位得到商。因为32=25,所以 k=5。
即结果为: ( x>=0 ? x : (x+31))>>5
但题目要求不能用比较和条件语句,因此要找一个计算偏移量b的方式
这里,x为正时b=0,x为负时b=31. 因此,可以从x的符号得到b
x>>31 得到的是32位符号,取出最低5位,就是偏移量b。
int div32(int x)
{ /* 根据x的符号得到偏移量b */
int b=(x>>31) & 0x1F;
return (x+b)>>5;
}
回顾:IA-32中的整数乘除指令
- 乘法指令:可给出一个、两个或三个操作数(生成OF和CF,不转异常处理)
- 若给出一个操作数SRC,则另一个源操作数隐含在AL/AX/EAX中,将SRC和累加器内容相乘,结果存放在AX(16位)或DX-AX(32位)或EDX-EAX(64位)中。DX-AX表示32位乘积的高、低16位分别在DX和AX中。 n位×n位=2n位
- 若指令中给出两个操作数DST和SRC,则将DST和SRC相乘,结果在DST中。n位× n位=n位
- 若指令中给出三个操作数REG、SRC和IMM,则将SRC和立即数IMM相乘,结果在REG中。n位× n位=n位
- 除法指令:只明显指出除数(判定是否异常并在异常时转异常处理)
- 若为8位,则16位被除数在AX寄存器中,商送回AL,余数在AH
- 若为16位,则32位被除数在DX-AX寄存器中,商送回AX,余数在DX
- 若为32位,则被除数在EDX-EAX寄存器中,商送EAX,余数在EDX
- 乘法和除法都区分带符号和无符号: MUL、IMUL,DIV、IDIV
第二讲小结
- 逻辑运算、移位运算、扩展运算等电路简单
- 主要考虑算术运算
- 定点运算涉及的对象
无符号数;带符号整数(补码);原码小数;移码整数 - 定点运算:(ALU实现基本算术和逻辑运算,ALU+移位器实现其他运算)
补码加/减:符号位和数值位一起运算,减法用加法实现。同号相加时可能溢出
原码加/减:符号位和数值位分开运算,用于浮点数尾数加/减运算
移码加减:移码的和、差等于和、差的补码,用于浮点数阶码加/减运算
- 定点运算涉及的对象
- 乘法运算:
无符号数乘法:“加”+“右移”
原码(一位/两位)乘法:符号和数值分开运算,数值部分用无符号数乘法实现,用于浮点数尾数乘法运算。
补码(一位/两位)乘法:符号和数值一起运算,采用Booth算法。
快速乘法器:流水化乘法器、阵列乘法器 - 除法运算:(试商用+/-、判断上商为1/0)
无符号数除法:用“加/减”+“左移” ,有恢复余数和不恢复余数两种。
原码除法:符号和数值分开,数值部分用无符号数除法实现,用于浮点数尾数除法运算。
补码除法:符号位和数值位一起。有恢复余数和不恢复余数两种。
快速除法器:很难实现流水化除法器,可实现阵列除法器,或用乘法实现
第三讲:3.5 浮点数运算
主 要 内 容
- 指令集中与浮点运算相关的指令( 以MIPS为例 )
- 涉及到的操作数
- 单精度浮点数
- 双精度浮点数
- 涉及到的运算
- 算术运算: 加 / 减 / 乘 / 除
- 涉及到的操作数
- 浮点数加减运算
- 浮点数乘除运算
- 浮点数运算的精度问题
有关Floating-point number的问题
- 实现一套浮点数运算指令,要解决的问题有:
- Issues:
- Representation(表示):
Normalized form (规格化形式) 和 Denormalized form
单精度格式 和 双精度格式 - Range and Precision (表数范围和精度)
- Arithmetic (+, -, *, / )
- Rounding(舍入)
- Exceptions (e.g., divide by zero, overflow, underflow)
(异常处理:如除数为0,上溢,下溢等) - Errors (误差) 与精度控制
- Representation(表示):
浮点数运算及结果
IEEE754标准规定的五种异常情况
- ① 无效运算(无意义)
- 运算时有一个数是非有限数,如:
加 / 减∞、0 x ∞、 ∞/∞等 - 结果无效,如:
源操作数是NaN、0/0、x REM 0、 ∞ REM y 等
- 运算时有一个数是非有限数,如:
- ② 除以0(即:无穷大)
- ③ 数太大(阶码上溢): 对于SP,阶码 E >1111 1110 (阶大于127)
- ④ 数太小(阶码下溢) : 对于SP,阶码 E < 0000 0001(阶小于-126-23 )
- ⑤ 结果不精确(舍入时引起),例如1/3,1/10等不能精确表示成浮点数
上述情况硬件可以捕捉到,因此这些异常可设定让硬件处理,也可设定让软件处理。让硬件处理时,称为硬件陷阱。 - 注:硬件陷阱:事先设定好是否要进行硬件处理(即挖一个陷阱),当出现相应异常时,就由硬件自动进行相应的异常处理(掉入陷阱)。
浮点数除0的问题
3.5.1 浮点数加/减运算
浮点数加/减运算-对阶
-
问题:如何对阶?
通过计算[ΔE]补来判断两数的阶差:
[ΔE]补= [Ex–Ey]补= [Ex]移+ [–[Ey]移]补 (mod 2n) -
问题:在ΔE为何值时无法根据[ΔE]补来判断阶差?溢出时!
例:4位移码,Ex=7,Ey=-7,则[ΔE]补=1111+1111=1110,ΔE<0,错 -
问题:对IEEE754 SP格式来说, |ΔE|大于多少时,结果就等于阶大的那个数(即小数被大数吃掉) ? 24!
1.xx…x → 0.00…01xx…x(右移24位后,尾数变为0)
因为尾数只有23+隐藏的1位, |ΔE|大于 24时,对阶时,阶小的就需要右移24位,有效数值被移出,所以结果就等于阶大的那个数
但是 ,由于可以保留附加位,阶小的那个数右移后的尾数可能会在舍入时向前面一位进 1。若等于25,则保留的附加位中,最左边第 1 位一定是 0,采用就近舍入时,这些附加位完全被丢弃。因此, 大于等于 25 时,可以使运算结果直接取阶大的那个数。 -
问题:IEEE754 SP格式的偏置常数是127,这会不会影响阶码运算电路的复杂度?
对计算[Ex–Ey]补 (mod 2n) 没有影响
[ΔE]补= 256+Ex–Ey=256+127+Ex– (127+Ey)
= 256 + [Ex]移– [Ey]移= [Ex]移+[–[Ey]移]补 (mod 256) -
但[Ex+Ey]移和 [Ex–Ey]移的计算会变复杂! 浮点乘除运算涉及之。
浮点数加减法基本要点
(假定:Xm、Ym分别是X和Y的尾数, Xe和Ye 分别是X和Y的阶码 )
- (1) 求阶差:∆e=Ye – Xe (若Ye > Xe,则结果的阶码为Ye[105])
- (2) 对阶:将Xm右移∆e位,尾数变为 Xm*2Xe-Ye(保留右移部分附加位)
- (3) 尾数加减: Xm*2Xe-Ye ± Ym
- (4) 规格化:
当尾数高位为0,则需左规:尾数左移一次,阶码减1,直到MSB为1或阶码为00000000(-126,非规格化数)
每次阶码减1后要判断阶码是否下溢(比最小可表示的阶码还要小)
当尾数最高位有进位,需右规:尾数右移一次,阶码加1,直到MSB为1
每次阶码加1后要判断阶码是否上溢(比最大可表示的阶码还要大) - (5) 如果尾数比规定位数长(有附加位),则需考虑舍入(有多种舍入方式)
- (6) 若运算结果尾数是0,则需要将阶码也置0。为什么?
- 尾数为0说明结果应该为0(阶码和尾数为全0)。
浮点数加法运算举例
- 例:用二进制浮点数形式计算 0.5 +(– 0.4375) =?
0.4375=0.25+0.125+0.0625=0.0111B- 解:0.5 = 1.000 x 2-1, - 0.4375 = -1.110 x 2-2
对 阶: -1.110 x 2-2 → -0.111 x 2-1
加 减: 1.000 x 2-1 +( -0.111 x 2-1 ) = 0.001 x 2-1
左 规: 0.001 x 2-1 → 1.000 x 2–4
判溢出: 无 - 结果为: 1.000 x 2–4 = 0.0001000 = 1/16 = 0.0625
- 解:0.5 = 1.000 x 2-1, - 0.4375 = -1.110 x 2-2
- 问题:为何IEEE 754 加减运算右规时最多只需一次?
因为即使是两个最大的尾数相加,得到的和的尾数也不会达到4,故尾数的整数部分最多有两位,保留一个隐含的“1”后,最多只有一位被右移到小数部分。
3.5.2浮点运算的精度和舍入
IEEE 754 浮点数加法运算
- 在计算机内部执行上述运算时,必须解决哪些问题?
- (1) 如何表示?
用IEEE754标准! - (2) 如何判断阶码的大小?求[ΔE]补=?
- (3) 对阶后尾数的隐含位如何处理?
右移到数值部分,高位补0,保留移出低位部分 - (4) 如何进行尾数加减?
隐藏位还原后,按原码进行加减运算,附加位一起运算 - (5) 何时需要规格化,如何规格化?
±1x .xx……x 形式时,则右规: 尾数右移1位, 阶码加1
± 0.0…01x…x 形式时,则左规: 尾数左移k位, 阶码减k - (6) 如何舍入?
最终须把附加位去掉,此时需考虑舍入(IEEE754有四种舍入方式) - (7) 如何判断溢出?
若最终阶码为全1,则上溢;若尾数(包含隐藏位)为全0,则下溢
- (1) 如何表示?
Extra Bits(附加位)
- "Floating Point numbers are like piles of sand; every time you move one you lose a little sand, but you pick up a little dirt.“
“浮点数就像一堆沙,每动一次就会失去一点‘沙’,并捡回一点 ‘脏’” - 如何才能使失去的“沙” 和捡回的“脏”都尽量少呢?在后面加附加位!
- 加多少附加位才合适?无法给出准确的答案!
- IEEE754规定: 中间结果须在右边加2个附加位 (guard & round)
Guard (保护位):在significand右边的位
Round (舍入位):在保护位右边的位 - 附加位的作用:用以保护对阶时右移的位或运算的中间结果。
- 附加位的处理: ①左规时被移到significand中; ② 作为舍入的依据。
Rounding Digits(舍入位)
-
举例:若十进制数最终有效位数为 3,采用两位附加位(G、R)。
-
问题:若没有舍入位,采用就近舍入到偶数,则结果是什么?
结果为2.36!精度没有2.37高! -
IEEE Standard: four rounding modes(用图说明)
-
round to nearest (default) 称为就近舍入到偶数
round digit < 1/2 then truncate (截断、丢弃)
> 1/2 then round up (末位加1)
= 1/2 then round to nearest even digit(最近偶数)
可以证明默认方式得到的平均误差最小。 -
round towards plus infinity (always round up)
-
round towards minus infinity (always round down)
-
round towards 0
-
IEEE 754的舍入方式的说明
- IEEE 754的舍入方式
( Z1和Z2分别是结果Z的最近的可表示的左、右两个数 )-
(1) 就近舍入:舍入为最近可表示的数
非中间值:0舍1入;
中间值:强迫结果为偶数-慢-
例如:附加位为
01:舍
11:入
10:(强迫结果为偶数) -
例:1.110111 → 1.1110; 1.110101 → 1.1101;
1.110110 → 1.1110; 1.111110 → 10.0000;
-
-
(2) 朝+∞方向舍入:舍入为Z2(正向舍入)
-
(3) 朝-∞方向舍入:舍入为Z1(负向舍入)
-
(4) 朝0方向舍入:截去。正数:取Z1; 负数:取Z2
-
浮点数舍入举例
-
例:将同一实数分别赋值给单精度和双精度类型变量,然后打印输出。
#include <stdio.h> main() { float a; double b; a = 123456.789e4; b = 123456.789e4; printf(“%f/n%f/n”,a,b); }
- 运行结果如下:
1234567936.000000
1234567890.000000 - 问题:为什么同一个实数赋值给float型变量和double型变量,输出结果会有所不同呢?
- 为什么float情况下输出的结果会比原来的大?这到底有没有根本性原因还是随机发生的?为什么会出现这样的情况?
- float可精确表示7个十进制有效数位,后面的数位是舍入后的结果,舍入后的值可能会更大,也可能更小
- 运行结果如下:
IEEE 754 浮点数加法运算举例
- 已知x=0.5, y=-0.4375, 求x+y=? (用IEEE754标准单精度格式计算)
- 解: x=0.5=1/2=(0.100…0)2=(1.00…0)2x2-1
y=-0.4325=(-0.01110…0)2=(-1.110…0)2x2-2 - [x]浮=0 01111110,00…0 [y]浮=1 01111101,110…0
- 对阶: [ΔE]补=[Ex-Ey]补=0111 1110 + 1000 0011=0000 0001,ΔE=1
故对y进行对阶:[y]浮=1 0111 1110 1110…0 000(用x的阶码,右移一位,高位补隐藏位、最后低位补两个附加位) - 尾数相加:001.0000…0000 + (100.1110…0000) = 00.00100…0
(原码加法,最左边一位为符号位,第二位防止有进位,符号位分开处理,最后三个附加位) - 左规: +(0.00100…0)2x2-1=+(1.00…0)2x2-4
(阶码减3,实际上是加了三次11111111([-1]补))
[x+y]浮=0 0111 1011 00…0
x+y=(1.0)2x2-4=1/16=0.0625 - 问题:尾数加法器最多需要多少位?
1+1+23+3=28位(防止有进位的一位,隐藏位,尾数小数位,附加位,符号位另算)
- 解: x=0.5=1/2=(0.100…0)2=(1.00…0)2x2-1
原码加/减运算
-
用于浮点数尾数运算
-
符号位和数值部分分开处理
-
仅对数值部分进行加减运算,符号位起判断和控制作用
-
规则如下:
- 比较两数符号,对加法实行“同号求和,异号求差”,对减法实行“异号求和,同号求差”。
- 求和:数值位相加,和的符号取被加数(被减数)的符号。若最高位产生进位,则结果溢出。
- 求差:被加数(被减数)加上加数(减数)的补码。
a) 最高数值位产生进位表明加法结果为正,所得数值位正确。
b) 最高数值位没产生进位表明加法结果为负,得到的是数值位的补码形式,需对结果求补,还原为绝对值形式的数值位。 - 差的符号位:a)情况下,符号位取被加数(被减数)的符号;
b)情况下,符号位为被加数(被减数)的符号取反。
-
例1:已知 [X]原 = 1.0011,[Y]原 = 1.1010,要求计算[X+Y]原
解:由原码加减运算规则知:同号相加,则求和,和的符号同被加数符号。
和的数值位为:0011 + 1010 = 1101 (ALU中无符号数相加)
和的符号位为:1
[X+Y]原 = 1.1101
求和:直接加,有进位则溢出,符号同被 -
例2 :已知 [X]原 = 1.0011,[Y]原 = 1.1010,要求计算[X–Y]原
解:由原码加减运算规则知:同号相减,则求差(补码减法)
差的数值位为:0011+(1010)求补 = 0011 + 0110 = 1001
最高数值位没有产生进位,表明加法结果为负,需对1001求补,还
原为绝对值形式的数值位。即:(1001)补= 0111
差的符号位为[X]原的符号位取反,即:0
[X–Y]原 = 0.0111
求差:加补码,不会溢出,符号分情况 -
思考题:如何设计一个基于加法器的原码加/减法器?
*3.5.3浮点数乘/除运算
浮点数乘/除法基本要点
求阶码的和、差
设Ex和Ey分别是两个操作数的阶码,Eb是结果的阶码,则:
- 阶码加法公式为: Eb ⬅ Ex+Ey+129 ( mod 28)
[E1+E2]移 = 127 + E1+ E2 = 127 + E1 + 127 + E2 –127
= [E1]移 + [E2]移 –127
= [E1]移 + [E2]移 +[–127] 补
= [E1]移 + [E2]移 +10000001B( mod 28) - 阶码减法公式为: Eb ⬅ Ex+[–Ey]补+127 ( mod 28)
[E1– E2]移 = 127 + E1– E2 = 127+E1–(127+E2)+127
= [E1]移–[E2]移 +127
= [E1]移+[–[E2]移]补+01111111B( mod 28)
举例
溢出判断
- 以下情况下,可能会导致阶码溢出
- 左规(阶码 - 1)时
- 左规(- 1)时:先判断阶码是否为全0,若是,则直接置阶码下溢;否则,阶码减1后判断阶码是否为全0,若是,则阶码下溢。
- 右规(阶码 +1)时
- 右规(+ 1)时,先判断阶码是否为全1,若是,则直接置阶码上溢;否则,阶码加1后判断阶码是否 为全1,若是,则阶码上溢。
- 左规(阶码 - 1)时
- 问题:机器内部如何减1?
+[-1]补 = + 11…1
举例
- 乘法运算求阶码的和时
- 若Ex和Ey最高位皆1,而Eb最高位是0或Eb为全1,则阶码上溢
- 若Ex和Ey最高位皆0,而Eb最高位是1或Eb为全0,则阶码下溢
- 除法运算求阶码的差时
- 若Ex的最高位是1,Ey的最高位是0,Eb的最高位是0或Eb为全1,则阶码上溢。
- 若Ex的最高位是0,Ey的最高位是1,Eb的最高位是1或Eb为全0,则阶码下溢。
- 例:若Eb = 0000 0001,则左规一次后,结果的阶码 Eb = ?
解:Eb=Eb+[-1]补=0000 0001 + 1111 1111 = 0000 0000 阶码下溢! - 例:若Ex=1111 1110,Ey=1000 0000,则乘法运算时结果的阶码 Eb=?
解:Eb=Ex+Ey+129=1111 1110+1000 0000+1000 0001=1111 1111
阶码上溢!
非规格化浮点数举例
C语言中的浮点数类型
- C语言中有float和double类型,分别对应IEEE 754单精度浮点数格式和双精度浮点数格式
- long double类型的长度和格式随编译器和处理器类型的不同而有所不同,IA-32中是80位扩展精度格式
- 从int转换为float时,不会发生溢出,但可能有数据被舍入
- 从int或 float转换为double时,因为double的有效位数更多,故能保留精确值
- 从double转换为float和int时,可能发生溢出,此外,由于有效位数变少,故可能被舍入
- 从float 或double转换为int时,因为int没有小数部分,所以数据可能会向0方向被截断
浮点数比较运算举例
回顾:IEEE 754 的范围和精度
举例
举例:Ariana火箭爆炸
- 1996年6月4日,Ariana 5火箭初次航行,在发射仅仅37秒钟后,偏离了飞行路线,然后解体爆炸,火箭上载有价值5亿美元的通信卫星。
- 原因是在将一个64位浮点数转换为16位带符号整数时,产生了溢出异常。溢出的值是火箭的水平速率,这比原来的Ariana 4火箭所能达到的速率高出了5倍。在设计Ariana 4火箭软件时,设计者确认水平速率决不会超出一个16位的整数,但在设计Ariana 5时,他们没有重新检查这部分,而是直接使用了原来的设计。
- 在不同数据类型之间转换时,往往隐藏着一些不容易被察觉的错误,这种错误有时会带来重大损失,因此,编程时要非常小心。
举例:爱国者导弹定位错误
-
1991年2月25日,海湾战争中,美国在沙特阿拉伯达摩地区设置的爱国者导弹拦截伊拉克的飞毛腿导弹失败,致使飞毛腿导弹击中了一个美军军营,杀死了美军28名士兵。其原因是由于爱国者导弹系统时钟内的一个软件错误造成的,引起这个软件错误的原因是浮点数的精度问题。
-
爱国者导弹系统中有一内置时钟,用计数器实现,每隔0.1秒计数一次。程序用0.1的一个24位定点二进制小数x来乘以计数值作为以秒为单位的时间
-
这个x的机器数是多少呢?
-
0.1的二进制表示是一个无限循环序列:0.00011[0011]…,x=0.000 1100 1100 1100 1100 1100B。显然,x是0.1的近似表示,0.1-x
= 0.000 1100 1100 1100 1100 1100 [1100]… -
0.000 1100 1100 1100 1100 1100B,即为:
=0.000 0000 0000 0000 0000 0000 1100 [1100]…B
=2-20×0.1 ≈9.54×10-8 -
已知在爱国者导弹准备拦截飞毛腿导弹之前,已经连续工作了100小时,飞毛腿的速度大约为2000米/秒,则由于时钟计算误差而导致的距离误差是多少?
100小时相当于计数了100×60×60×10=36×105次,因而导弹的时钟已经偏差了9.54×10-8×36×105 ≈0.343秒
因此,距离误差是2000×0.343秒 ≈687米 -
小故事:实际上,以色列方面已经发现了这个问题并于1991年2月11日知会了美国陆军及爱国者计划办公室(软件制造商)。以色列方面建议重新启动爱国者系统的电脑作为暂时解决方案,可是美国陆军方面却不知道每次需要间隔多少时间重新启动系统一次。1991年2月16日,制造商向美国陆军提供了更新软件,但这个软件最终却在飞毛腿导弹击中军营后的一天才运抵部队。
-
若x用float型表示,则x的机器数是什么?0.1与x的偏差是多少?系统运行100小时后的时钟偏差是多少?在飞毛腿速度为2000米/秒的情况下,预测的距离偏差为多少?
- 0.1= 0.0 0011[0011]B=+1.1 0011 0011 0011 0011 0011 00B×2-4,故x的机器数为0 011 1101 1 100 1100 1100 1100 1100 1100
- Float型仅24位有效位数,后面的有效位全被截断,故x与0.1之间的误差为:|x–0.1|=0.000 0000 0000 0000 0000 0000 0000 1100 [1100]…B。这个值等于2-24×0.1 5.96×10-9。100小时后时钟偏差5.96×10-9×36×105 0.0215秒。距离偏差0.0215×200043米。比爱国者导弹系统精确约16倍。
举例:浮点数运算的精度问题
- 从上述结果可以看出:
- 用32位定点小数表示0.1 ,比采用float精度高64倍
- 用float表示在计算速度上更慢,必须先把计数值转换为IEEE 754格式浮点数,然后再对两个IEEE 754格式的数相乘,故采用float比直接将两个二进制数相乘要慢
- Ariana 5火箭和爱国者导弹的例子带来的启示
- 程序员应对底层机器级数据的表示和运算有深刻理解
- 计算机世界里,经常是“差之毫厘,失之千里”,需要细心再细心,精确再精确
- 不能遇到小数就用浮点数表示,有些情况下(如需要将一个整数变量乘以一个确定的小数常量),可先用一个确定的定点整数与整数变量相乘,然后再通过移位运算来确定小数点
实例: PowerPC和80x86中的浮点部件
- PowerPC中的浮点运算
- 比MIPS多一条浮点指令:乘累加指令
- 将两个操作数相乘,再与另一个操作数相加,作为结果操作数
- 可用一条乘累加指令代替两条MIPS浮点指令
- 可为中间结果多保留几位,得到最后结果后再考虑舍入,精度高
- 利用它来实现除法运算和平方根运算
- 浮点寄存器的数量多一倍(32xSPR, 32xDPR)
- 比MIPS多一条浮点指令:乘累加指令
- 80x86中的浮点运算
- 采用寄存器堆栈结构:栈顶两个数作为操作数
- 寄存器堆栈的精度为80位 (MIPS和PowerPC是32位或64位)
- 所有浮点运算都转换为80位扩展浮点数进行运算,写回存储器时,再转换位32位(float)或64位(double),编译器需小心处理
- 由浮点数访存指令自动完成转换
- 指令类型:访存、算术、比较、函数(正弦、余弦、对数等)
第三讲小结
- 浮点数的表示(IEEE754标准)
- 单精度SP(float)和双精度DP(double)
- 规格化数(SP):阶码1~254,尾数最高位隐含为1
- 0(阶为全0,尾为全0)
- ∞(阶为全1,尾为全0)
- NaN(阶为全0,尾为非0)
- 非规数(阶为全1,尾为非0)
- 单精度SP(float)和双精度DP(double)
- 浮点数加减运算
- 对阶、尾数加减、规格化(上溢/下溢处理)、舍入
- 浮点数乘除运算
- 求阶、尾数乘除、规格化(上溢/下溢处理) 、舍入
- 浮点数的精度问题
- 中间结果加保护位、舍入位(和粘位)
- 最终进行舍入(有四种舍入方式)
- 就近(中间值强迫为偶数)、+ ∞方向、- ∞方向、0方向
- 默认为“就近”舍入方式
本章总结
定点数运算:由ALU + 移位器实现各种定点运算
- 移位运算
- 逻辑移位:对无符号数进行,左(右)边补0,低(高)位移出
- 算术移位:对带符号整数进行,移位前后符号位不变,编码不同,方式不同。
- 循环移位:最左(右)边位移到最低(高)位,其他位左(右)移一位。
- 扩展运算
- 零扩展:对无符号整数进行高位补0
- 符号扩展:对补码整数在高位直接补符
- 加减运算
- 补码加/减运算:用于整数加/减运算。符号位和数值位一起运算,减法用加法实现。同号相加时,若结果的符号不同于加数的符号,则会发生溢出。
- 原码加/减运算:用于浮点数尾数加/减运算。符号位和数值位分开运算,同号相加,异号相减;加法直接加;减法用加负数补码实现。
- 乘法运算:用加法和右移实现。
- 补码乘法:用于整数乘法运算。符号位和数值位一起运算。采用Booth算法。
- 原码乘法:用于浮点数尾数乘法运算。符号位和数值位分开运算。数值部分用无符号数乘法实现。
- 除法运算:用加/减法和左移实现。
- 补码除法:用于整数除法运算。符号位和数值位一起运算。
- 原码除法:用于浮点数尾数除法运算。符号位和数值位分开运算。数值部分用无符号数除法实现。
- 浮点数运算:由多个ALU + 移位器实现
- 加减运算
- 对阶 、尾数相加减、规格化处理、舍入、判断溢出
- 乘除运算
- 尾数用定点原码乘/除运算实现,阶码用定点数加/减运算实现。
- 溢出判断
- 当结果发生阶码上溢时,结果发生溢出,发生阶码下溢时,结果为0。
- 精确表示运算结果
- 中间结果增设保护位、舍入位、粘位
- 最终结果舍入方式:就近舍入 / 正向舍入 / 负向舍入 / 截去四种方式。
- ALU的实现
- 算术逻辑单元ALU:实现基本的加减运算和逻辑运算。
- 加法运算是所有定点和浮点运算(加/减/乘/除)的基础,加法速度至关重要
- 进位方式是影响加法速度的重要因素
- 并行进位方式能加快加法速度
- 通过“进位生成”和“进位传递”函数来使各进位独立、并行产生