上溢、下溢与除以零等异常情况在大多数系统中是经常发生的。以双精度浮点数为例,如果只考虑格式化数,则它的溢出有四种情况:
(1)正数大于(1-)×2 的情况为正上溢;
(2)正数小于0.5×2的情况为正下溢;
(3)负数小于-(1-2)×2的情况为负上溢;
(4)负数大于0.5×2的情况为负下溢。
在浮点运算过程中一个很重要的步骤是舍入。舍入的目的是使得有效数据的位数保持在固定位数范围内。
IEEE754标准规定了四种舍入模式:
(1)就近舍入(偶数):目标是使舍入结果尽可能接近实际值。最大误差为±LSB(Least Significant Bit)。
(2)零舍入RZ:通过截断实际值的多余位来实现。最大误差为土LSB。
(3)+∞舍入:结果向正无穷大方向舍入。所有值被舍入到下一个可能值,负数的舍入结果将截去多余位,正数的舍入结果为下一个较大的有效值。
(4)-∞舍入:结果向负无穷大方向舍入。负数的舍入结果将为下一个较小的有效值,正数的舍入结果将截去多余位。
下表给出了不同的舍入方法下的舍入结果,以8位值舍入为4位值为例。
不同舍入方法的舍入值
数值 | 就近舍入 | 零舍入 | +∞舍入 | -∞舍入 |
.01101001 | .0111 | .0110 | .0111 | .0110 |
-.01101001 | -.0111 | -.0110 | -.0110 | -.0111 |
.10000111 | .1000 | .1000 | .1001 | .1000 |
-10000111 | -.1000 | -.1000 | -.1000 | -.1001 |
.10000000 | .1000 | .1000 | .1000 | .1000 |
浮点运算单元源代码:
module alu(flout_a,flout_b,clk,en,rst,flout_c,yichu); //浮点运算单元模块
input[31:0] flout_a; //输入的被乘数
input[31:0] flout_b; //输入的乘数
input clk; //时钟信号
input en; //使能信号
input rst; //复位信号
output[31:0] flout_c; //输出运算结果
output[1:0] yichu; //输出溢出标志
reg[31:0] flout_c;
reg[1:0] yichu;
reg[1:0] overflow;
reg s3;
reg s1,s2; //符号
reg[7:0] exp1,exp2,exp3; //阶码
reg[22:0] man1,man2,man3; //尾数
reg n;
reg[7:0] temp1;
reg[7:0] temp2;
reg[8:0] temp3;
reg[23:0] temp;
reg[45:0] comeout;
reg[23:0] all; //小数部分
reg[1:0] zheng; //整数部分
always@(posedge clk) //提取flout_a的符号,阶码,尾数
begin
if(!rst)
begin
s1<=1'b0;
exp1<=8'b00000000;
man1<=23'b0;
end
else if(en)
begin
s1<=flout_a[31];
exp1<=flout_a[30:23];
man1<=flout_a[22:0];
end
end
always @(posedge clk) //提取flout_b的符号,阶码,尾数
begin
if(!rst)
begin
s2<=1'b0;
exp2<=8'b00000000;
man2<=23'b0;
end
else if(en)
begin
s2<=flout_b[31];
exp2<=flout_b[30:23];
man2<=flout_b[22:0];
end
end
always@(posedge clk) //尾数相乘
begin
if(man1==23'b0000000000_0000000000000)
begin
man3=man1;
n=1'b0;
end
else if(man2==23'b0000000000_0000000000000)
begin
man3=man2;
n=1'b0;
end //处理特殊值
else
begin
comeout=man1*man2;
temp=man1+man2; //1.m*1.n=1+(0.m+0.n)+(0.m*0.n)
all=temp[22:0]+comeout[45:23];
zheng=1'b1+temp[23];
zheng=zheng+all[23]; //整数
if(zheng[1]==1) //整数小于4而大于1
begin
n=1'b1; //左归阶码应加1
if(zheng[0]==1)
man3[22:0]={2'b1,all[22:1]}; //零舍入
else
man3[22:0]={2'b0,all[22:1]};
end
else
begin
n=1'b0;
man3=all[22:0];
end
end
end
always@(posedge clk) //阶码相加
begin
if(exp1[7]==1)
temp1={1'b0,exp1[6:0]};
else temp1={1'b1,exp1[6:0]};
if(exp2[7]==1)
temp2={1'b0,exp2[6:0]};
else temp2={1'b1,exp2[6:0]}; //把阶码的移码形式变为补码形式
temp3=temp1+temp2;
temp3=temp3+n;
if(temp3[8:7]==2'b01)
overflow=2'b01; //阶码上溢
else
begin
if(temp3[8:7]==2'b10)
overflow=2'b10; //阶码下溢
else overflow=2'b00;
end
case(temp3[8:7])
2'b00:exp3={1'b1,temp3[6:0]};
2'b01:exp3=temp3[7:0];
2'b11:exp3={1'b0,temp3[6:0]};
2'b10:exp3=temp3[7:0];
endcase
end
always@(posedge clk) //输出结果
begin
s3=s1^s2;
yichu= overflow;
if(man3==0)
begin
flout_c=32'b0;
yichu=2’b00;
end
else flout_c={s3,exp3[7:0],man3[22:0]};
end
endmodule
¥29.8
¥9.9
¥59.8