浮点数加法器设计

一.设计内容及其要求

内容

根据浮点数的运算规则和IEEE754标准,实现浮点数的加法运算器。

要求:

  • 浮点数格式:符号位1位,阶码8位,尾数23位
  • 要求该实验能完整的体现浮点数的运算的五个步骤:对阶、尾数求和、规格化、舍入(要求使用对偶舍入)、溢出判断。

二.基本要素

浮点数

浮点数即实数,实数是所有有理数和无理数的集合。它之所以被称作浮点数,是因为小数点在数中的位置并不是固定的。一个浮点数值分为两部分存储:数值以及小数点在数值中的位置。

计算机中的浮点运算的计算结果一般是不确定的,一块芯片的浮点运算结果可能和另一块芯片的运算结果是不一样的。

科学计数法

科学计数法用来表示很大或很小的数。

十进制浮点数可以表示为: 尾 数 × 1 0 指 数 , 如 1.23456 × 1 0 20 尾数×10^{指数},如1.23456×10^{20} ×101.23456×1020

二进制浮点数可以表示为: 尾 数 × 2 指 数 , 如 1.00011 × 2 5 尾数×2{指数},如1.00011×2^{5} ×21.00011×25

IEEE 754浮点数标准提供3中浮点数表示:32位单精度浮点数、64位双精度浮点数、128位四精度浮点数。这里设计浮点加法器主要用32位单精度浮点数,想要实现64位或者128位浮点数的加法,它们的原理和32位单精度浮点数的加法器是类似的。

规格化浮点数

IEEE 754浮点数的尾数总是规格化的,其范围为 1.000...0 × 2 e − 1.111...1 × 2 e 1.000...0×2^e - 1.111...1×2^e 1.000...0×2e1.111...1×2e,e为指数。

规格化浮点数的最高位总是1,规格化使尾数的所有位都是有效的,因而尾数精度更高。

如: 0.10... × 2 e 规 格 化 为 1.10... × 2 e − 1 10.1... 2 e 规 格 化 为 1.01... × 2 e + 1 0.10...×2^e规格化为1.10...×2^{e-1}\\10.1...2^e规格化为1.01...×2^{e+1} 0.10...×2e1.10...×2e110.1...2e1.01...×2e+1

尾数规格化充分利用了可用的最大精度。如,一个8位非规格话的尾数0.0000101只有4位有效位,而规格化后的8位尾数1.0100011则有8位有效位。

偏置指数

IEEE 754浮点数的尾数被表示为符号及值的形式,即用一个符号位表示它是正数还是负数。它的指数则用偏置方式表示,即给真正的指数加上一个常数。

已知32位单精度浮点数的指数部分是8位,则偏置值为127。如果一个数的指数为0,则被保存为 0 + 127 = 127 0+127=127 0+127=127。如果指数为-2,则被保存为 − 2 + 127 = 125 -2+127=125 2+127=125

实数1010.1111规格化的结果为 1.010111 × 2 3 1.010111×2^3 1.010111×23,指数为3,将被保存为 3 + 127 = 130 3+127=130 3+127=130

这种用偏置表示指数的方法优点在于:最小的负指数被表示为0,如果不采用这种方法,0的浮点表示为 0.0...0 × 2 最 小 负 指 数 0.0...0×2^{最小负指数} 0.0...0×2 。采用偏置指数。0就可以用尾数0和指数0表示:
在这里插入图片描述

IEEE浮点数

内容

一个32位IEEE 754单精度浮点数可以被表示为下面的二进制串:

S EEEEEEEE 1.MMMMMMMMMMMMMMMMMMMMMMM

  • S为符号位,指明这个数是正数还是负数
  • E为8位偏置指数,指出了小数点的位置
  • M为23位尾数

在这里插入图片描述

S作为符号位,决定了数的符号,若S=0,则为正数,若S=1,则为负数。指数E将浮点数的尾数扩大或缩小 2 E 2^E 2E 倍,并且偏置值为127。

如浮点数 1.11001...0 × 2 12 1.11001...0×2^{12} 1.11001...0×212的指数为 12 + 127 = 13 9 10 = 1000101 1 2 12+127=139_{10}=10001011_2 12+127=13910=100010112

IEEE浮点数的尾数总是规格化的,其值范围在 1.0000..00 − 1.1111..11 1.0000..00-1.1111..11 1.0000..001.1111..11 ,除非这个浮点数是0,此时尾数为 0.000..00 0.000..00 0.000..00

由于尾数总是规格化的,且最高位总是为1,因此将尾数存入存储器时没有必要保存最高位的1。所以,一个非0的IEEE 754浮点数可被定义为: X = ( − 1 ) S × 2 E − B × 1. F X=(-1)^S×2^{E-B}×1.F X=(1)S×2EB×1.F ,其中:

  • S符号位
  • E:偏置量为B的指数
  • F:尾数的小数部分(实际的尾数为1.F,有个隐含的1)

ps:浮点数0被表示为S=0,E=0,M=0(即浮点数0用全0表示)

格式

IEEE 754标准定义了基本的和扩展的浮点数格式,以及一组数量有限的算术运算的规则(加、减、乘、除、平方根、求余和比较)。

非数(Not a Number,NaN)是IEEE 754标准提供的一个专门的符号,表示IEEE 754标准格式所不能表示的数。

img

img

img

img

image

在32位IEEE 754单精度浮点数格式中,最大指数 E m a x E_{max} Emax为127,最小指数 E m i n E_{min} Emin为-126,而不是 − 127 至 128 -127至128 127128 E m i n − 1 ( 即 − 127 ) E_{min-1}(即-127) Emin1(127)用来表示浮点0, E m a x + 1 ( 即 128 ) E_{max}+1(即128) Emax+1128用来表示正/负无穷大或NaN数。

下图,描述了IEEE单精度浮点数格式,指数E=0和E=255等特例分别被用于表示浮点0,非规格化效数、正/负无穷大、以及NaN:

在这里插入图片描述

特点

(1)浮点数接近0时的特点,下图描述了一个指数2位,尾数为2位的浮点数系统。浮点数0表示00 000,下一个规格化的正数表示为00 100(即 2 − b × 1.00 2^{-b}×1.00 2b×1.00,b为偏置常数):
在这里插入图片描述

浮点数0附近有一块禁止区,其中的浮点数都是非规格化的,因此无法被表示为IEEE标准格式。这个数的指数和起始位都是0的区域,也可用来表示浮点数。但是这些数都是非规格化的,其精度比规格化的精度低,会导致渐进式下溢。

(2)IEEE标准规定,缺省的舍入技术应该向最近的值舍入。

(3)IEEE标准规定了4种比较结果,分别是等于、小于、大于和无序,无序用于一个操作数是NaN数的情景。

(4)IEEE标准规定了5种异常:

  • 操作数不合法:使用了一些不合法的操作数:NaN数、无穷大数、求负数的平方根(这里不必考虑)。
  • 除数为0(这里是加法器,所以不需要考虑)
  • 上溢:当结果比最大浮点数还大时,处理上溢的方法有终止计算和饱和运算(用最大值作为结果)等
  • 下溢:当结果比最小浮点数还小时,处理下溢的方法有将最小浮点数设为0或用一个小于 2 E m i n 2^{E_{min}} 2Emin的非规格化数表示最小浮点数等方式处理。
  • 结果不准确:当某个操作产生舍入错误时。

运算

浮点数不能直接运算。

以一个简单的8位尾数和一个未对齐的指数为例说明浮点运算, A = 1.0101001 × 2 4 A=1.0101001×2^4 A=1.0101001×24 B = 1.1001100 × 2 3 B=1.1001100×2^3 B=1.1001100×23。若要计算两个数的成绩,应将尾数相乘,指数相加: A ⋅ B = 1.010100 × 2 4 × 1.1001100 × 2 3           = 1.0101001 × 1.1001100 × 2 3 + 4           = 1.000011010101100 × 2 8 A·B=1.010100×2^4× 1.1001100×2^3\\~~~~~~~~~=1.0101001×1.1001100×2^{3+4}\\~~~~~~~~~=1.000011010101100×2^8 AB=1.010100×24×1.1001100×23         =1.0101001×1.1001100×23+4         =1.000011010101100×28

由于IEEE标准下的浮点操作数已被表示为规格化形式,计算机在进行浮点加法时,为了对齐指数,计算机必须执行下面步骤:

  • 第一步,找出指数较小的数
  • 第二部,使两个数的指数相同
  • 第三步,尾数相加
  • 第四步,如果有必要,将结果规格化

所以得到A+B的运算

在这里插入图片描述

注意:

(1)因为IEEE754标准的32位单精度浮点数的指数与尾数位于位于同一个字中,所以在加法过程开始之前必须将它们分离开。

(2)如果两个指数的差大于p+1,p为尾数的位数,这里p=23,较小的数由于太小而无法影响较大的数,结果实际就等于较大的数。

(3)结果规格化时检查指数范围,以分别检测指数下溢或上溢。指数下溢会导致结果为0,而指数上溢出会造成错误。

浮点加法运算的流程图

在这里插入图片描述

舍入和截断误差

内容介绍

浮点运算可能引起尾数位数的相加,需要保持尾数位数不变的方法。最简单的技术叫作截断。

比如将0.1101101截断为4位尾数的结果为0.1101。截断会产生诱导误差(即误差是由施加在数上的操作计算所引起的),诱导误差是偏差的,因为截断后的数总比截断钱小。

舍入是一种更好的减少数的尾数的技术。如果舍弃的位的值大于剩余数的最低位的一半,将剩余数的最低位+1。

比如:
在这里插入图片描述

舍入机制

(1)最简单的舍入机制是截断或向0舍入。

(2)向最近的数舍入:选择距离该数最近的那个浮点数作为结果。

(3)向正或负无穷大舍入:选择正或负无穷大方向上最近的有效浮点数作为结果。

(4)向偶数舍入:当要舍入的数位于两个连续浮点数的正中时,IEEE舍入机制选择最低位为0的点(即向偶数舍入)

在这里插入图片描述

舍入机制将在后面基本原理与设计部分重点解释。

三.基本原理与设计

定义接口部分:时钟信号clk,复位信号rst,输入[31:0]宽的加数x和被加数y,输出[31:0]宽的结果z,[1:0]宽的溢出标志overflow。

其中overflow=2’b00:没有溢出; overflow=2’b01:上溢; overflow=2’b10:下溢; overflow=2’b11:输入不是规格化数

定义[24:0]宽的尾数部分m_x,m_y,m_z:IEEE 754标志的32位单精度浮点数的尾数本身是23位宽的,但是再尾数相加的时候要考虑到隐含的1,并且还需要考虑进位,所以设定为25位宽的数组来存储的尾数。

定义[7:0]宽的指数部分exponent_x,exponent_y,exponent_z。

定义符号部分sign_z,sign_x,sign_y。

这里实现的是多周期的单精度浮点加法器,所以考虑到需要状态机,所以设定[2:0]宽的state_now,state_next分别表示当前状态和下一个状态,并且设定start = 3’b000(start是初始化阶段),zerocheck = 3’b001(zerocheck是检查x=0或y=0阶段),equalcheck = 3’b010(equalcheck对阶阶段),addm = 3’b011(addm是尾数相加阶段),normal = 3’b100(normal是规格化尾数阶段),over = 3’b110(over是判断溢出阶段,此阶段同时输出结果部分);

由于需要进行对偶数舍入所以这里设定[24:0]宽的尾数移出部分out_x,out_y来存储x与y右移出尾数的部分(相对顺序不变),设定[24:0]宽的最低位的一半的部分mid_x,mid_y来存储x与y尾数的最低有效位的一半,设定[2:0]宽的大小标志bigger(如果exponent_x>exponent_y,则bigger=2’b01;如果exponent_x<exponent_y,则bigger=2’b10 ;如果exponent_x=exponent_y,则bigger=2’b00),设定[7:0]宽的移动步数记录部分move_tot。

初始化阶段start

分离指数和尾数以及符号位,并对out_x,mid_x,move_tot进行相应的初始化。判断加数和被加数是否是规格化浮点数,如果不是,直接置overflow=2’b11,重新进入初始化阶段,进行下一组数的加法。

case(state_now)
            start:begin//分离指数和尾数 
                exponent_x<=x[30:23];
                m_x<={1'b0,1'b1,x[22:0]};//最高位来控制溢出 1.F
                out_x<=25'b0;
                mid_x<={24'b0,1'b1};
                move_tot<=8'b0;
                ...
                ...
                //只有阶码[1,254]和实数0是规格化数字
                if((exponent_x==8'd255)||(exponent_y==8'd255))begin
                    //当x或者y是无穷大的时候,这并不是一个规格化数字,应该令答案为无穷大,并标记overflow=2'b11,跳转到over阶段
                    ...
                end
                else if((exponent_x==8'd0&&m_x[22:0]!=23'b0)||(exponent_y==8'd0&&m_y[22:0]!=23'b0))begin
                    //当x或者y是非规格化数字的时候(小于最小的规格化数字),直接将结果赋值为非规约数,然后回到over阶段
                    // z<=32'hFFFFFFFF;
                end
                else begin
                    overflow<=2'b0;
                    state_next<=zerocheck;//进入判断0阶段
                end
            end

检查加数和被加数是否为0阶段zerocheck

根据IEEE754标准浮点数为0的条件是指数部分和尾数部分均为0。如果x=0,则z=y,直接进入最后的溢出标志阶段输出结果;如果y=0,则z=x,直接进入最后的溢出标志阶段输出结果。如果不存在0则进入下一阶段—对阶阶段。

zerocheck:begin//x==0||y==0,则直接进入over
                if(m_x[22:0]==23'b0&&exponent_x==8'b0)begin
                	...
                end
                else if(m_y[22:0]==23'b0&&exponent_y==8'b0)begin
                	...
                end
                else begin
                	...
                end
            end

对阶阶段equalcheck

这个阶段需要进行尾数的舍入,这里需要再次强调一下舍入的规则:

舍入的规则需要区分三种情况:

  • 当具体的值大于中间值的时候,向上舍入
  • 当具体的值小于中间值的时候,向下舍入
  • 当具体的值等于中间值的时候,向偶数舍入。向偶数舍入指的是要保留的最低有效位为偶数,具体规则:
    • 要保留的最低有效位如果为奇数,则向上舍入
    • 要保留的最低有效位如果为偶数,则向下舍入

什么是中间值?

上面的舍入规则,提到了一个很重要的概念,中间值。怎样才能确定这个中间值呢?

要找到中间值,先确定要保留的有效数字,找到要保留的有效数字最低位的下一位。如果这位是进制的一半,而且之后的位数都为 0,则这个值就是中间值。

例子:

  • 十进制的 1.2500,要保留到小数点后一位,下一位是 5,是进制的一半,后面位数都为 0,所以这个值就是中间值
  • 二进制的 10.0110,要保留到小数点后两位,下一位是 1,是进制的一半,后面位数都为 0,所以这个值就是中间值

知道了舍入规则之后,看几个具体的例子,以二进制为例,有效位数保留到小数点后两位。

  • 10.00011,中间值为 10.00100,小于中间值,向下舍入为 10.00

  • 10.00110,中间值为 10.00100,大于中间值,向上舍入为 10.01

  • 10.11100,中间值为 10.11100,等于中间值,要保留的最低有效位 1 为奇数,向上舍入为 11.00

  • 10.10100,中间值为 10.10100,等于中间值,要保留的最低有效位 0 为偶数,向下舍入为 10.10

这一阶段主要比较加数和被加数的指数部分的大小,并以此为根据进行尾数的相应操作。

  • 如果指数部分相同(即exponent_x==exponent_y)则进入尾数相加阶段。
  • 如果指数部分不同
    • exponent_x>exponent_y,则将y的尾数部分m_y右移,同时把右移出去的尾数部分按照原来的相应顺序存入out_y中,这个相应顺序对应的下标可以用move_tot进行跟踪。每次只右移一次,并且下一状态依旧是equalcheck阶段,直到exponent_x==exponent_y才进入尾数相加阶段。值得注意的是,如果exponent_x和exponent_y相差过大,可能导致y不断右移直到全0,y的影响可以忽略不记,这时需要进行特殊的判断,使得z=x,并且直接进入over阶段。当exponent_x == exponent_y的时候需要进行舍入机制的判断,在尾数移位的时候已经处理出了尾数移出部分out_y和最低有效位的一半mid_y,根据上述的舍入规则进行相应的操作即可。
    • exponent_x<exponent_y,方法和exponent_x>exponent_y类似,不再赘述。
equalcheck:begin//对阶处理 需要在移位的时候进行向偶舍入
                if(exponent_x==exponent_y)begin
                    if(bigger==2'b00)begin
                        ...//指数对齐,进入尾数加阶段
                    end
                    else if(bigger==2'b01)begin
                    	...//对y的尾数部分进行舍入并进入尾数加阶段
                    end
                    else if(bigger==2'b10)begin
                    	...//对x的尾数部分进行舍入并进入尾数加阶段
                    end
                end
                else begin
                    if(exponent_x>exponent_y)begin
                    	...//y的尾数右移动阶段,并且记录移出的部分
                        if(m_y==24'b0)begin//如果x和y的节码差距过大,就不需要考虑太小的
                            sign_z<=sign_x;
                            exponent_z<=exponent_x;
                            m_z<=m_x;
                            state_next<=over;
                        end
                        else begin
                            state_next<=equalcheck;
                        end
                    end
                    else begin
                        ...//x的尾数右移动阶段,并且记录移出的部分
                        if(m_x==24'b0)begin//如果x和y的节码差距过大,就不需要考虑太小的
                            sign_z<=sign_y;
                            exponent_z<=exponent_y;
                            m_z<=m_y;
                            state_next<=over;
                        end
                        else begin
                            state_next<=equalcheck;
                        end
                    end
                end
            end

尾数相加阶段addm

此时加数和被加数的指数部分相同,则z的指数部分也是和它们相同的。

根据加数和被加数的符号位判断是同号相加还是异号相加。

如果x和y同号,则z与它们同号,尾数部分m_z=m_x+m_y。

如果x和y异号,则z的符号就是较大的数的符号,尾数部分m_z=较大数的尾数-较小数的尾数。

注意如果最后z的尾数全0则没必要进入规格化尾数阶段,直接进入over阶段即可。

addm:begin
                if(x[31]^y[31]==1'b0)begin
                    ...//加数和被加数同号
                    state_next<=normal;
                end
                else begin
                    ...//加数和被加数异号
                    if(m_x>m_y)begin
                        ...//x为较大的数
                        state_next<=normal;
                    end
                    else if(m_x<m_y)begin
                        ...//y为较大的数
                        state_next<=normal;
                    end
                    else begin
                        ...//x与y相等
                        m_z<=23'b0;
                        state_next<=over;//全零没必要规格化
                    end
                end
            end

规格化尾数阶段normal

规格化尾数,如果存在进位则m_z[24]=1,这时需要进行右移操作,指数部分+1,然后进入over阶段。如果不存在进位,则检查是否需要右规(判断条件是m_z[23]是否等于0,m_z[23]就是1.F中的1的那个位置),如果需要则指数部分-1,尾数右移。

normal:begin
                if(m_z[24]==1'b1)begin
                    // 存在进位/借位
                    ...
                    state_next<=over;
                end
                else begin
                    if(m_z[23]==1'b0)begin//规格化处理,0.xxxx转化成1.xxxxx
                    	//右规处理
                        state_next<=normal;
                    end
                    else begin
                        state_next<=over;
                    end
                end
            end

判断溢出阶段over

此阶段需要判断溢出(上溢、下溢)以及赋值给结果z。

上溢:大于最大浮点数,标记overflow=2’b01;

下溢:小于最小浮点数(这部分本身是非规约数),直接输出z,标记overflow=2’b10;

其余情况标记overflow=2’b00;状态回到start阶段。

over:begin
                z={sign_z,exponent_z[7:0],m_z[22:0]};
                //判断溢出:
                //1.大于最大浮点数
                //2.小于最小浮点数(这部分本身是非规约数),直接输出z,标记出它是下溢出
                if(overflow)begin
                    ...//这部分必然是由start阶段直接跳转过来的非规格化数字。
                    state_next<=start;
                end
                else if(exponent_z==8'd255)begin
                    //1.大于最大浮点数
                	...
                    state_next<=start;
                end
                else if(exponent_z==8'd0&&m_z[22:0]!=23'b0)begin
                    //2.小于最小浮点数(这部分本身是非规约数),直接输出z,标记出它是下溢出
                	...
                    state_next<=start;
                end
                else begin
                    overflow<=2'b00;
		            state_next<=start;
                end 
            end

时钟上升沿控制模块

当时钟上升沿到来的时候:如果复位信号置0,则当前状态变成start状态;其余时候当前状态转为下一状态(即state_now=state_next)。

always @(posedge clk) begin
        if(!rst)begin
            state_now<=start;
        end
        else begin
            state_now<=state_next;
        end
    end

四.功能验证

对于浮点加法器的功能验证我们需要考虑如下的情况:

  • 正常范围的正数相加
  • 正常范围的负数相加
  • 正常范围的整数相加
  • 正常范围的一正一负相加(|正|>|负|)
  • 正常范围的一正一负相加(|正|<|负|)
  • 两数相加造成上溢
  • 两数相加造成下溢
  • 加数出现无穷大
  • 加数出现非数(NaN)
  • 加数出现非规约形式的浮点数
  • 一个加数为零
  • 两个加数相差过大

接下来将逐一进行验证:

(1)正常范围的正数相加:

x = 0.78 ( 3 F 47 A E 14 ) , y = 0.55 ( 3 F 0 C C C C D ) x=0.78(3F47AE14),y=0.55(3F0CCCCD) x=0.78(3F47AE14),y=0.55(3F0CCCCD)

期望: z = 1.3 z=1.3 z=1.3

实际: z = 3 F A A 3 D 70 ( 1.3299999237061 ) z=3FAA3D70(1.3299999237061) z=3FAA3D70(1.3299999237061)

在这里插入图片描述

x = 50.2 ( 4248 C C C C ) , y = 1.1 ( 3 F 8 C C C C C ) x=50.2(4248CCCC),y=1.1(3F8CCCCC) x=50.2(4248CCCC),y=1.1(3F8CCCCC)

期望: z = 51.3 z=51.3 z=51.3

实际: z = 424 D 3332 ( 51.299995422363 ) z=424D3332(51.299995422363) z=424D3332(51.299995422363)

在这里插入图片描述

(2)正常范围的负数相加

x = − 2.032883758613 E − 20 ( 9 E C 0001 D ) , y = − 1.0842014616272 E − 19 ( 9 F F F F F F 5 ) x=-2.032883758613E-20(9EC0001D),y=-1.0842014616272E-19(9FFFFFF5) x=2.032883758613E20(9EC0001D),y=1.0842014616272E19(9FFFFFF5)

期望: z = − 1.2874898374885 E − 19 z=-1.2874898374885E-19 z=1.2874898374885E19

实际: z = A 017 F F F E ( − 1.2874898213326 E − 19 ) z=A017FFFE(-1.2874898213326E-19) z=A017FFFE(1.2874898213326E19)

在这里插入图片描述

(3)正常范围的整数相加

x = 100 ( 42 C 80000 ) , y = 200 ( 43480000 ) x=100(42C80000),y=200(43480000) x=100(42C80000),y=200(43480000)

期望: z = 300 z=300 z=300

实际: z = 43960000 ( 300 ) z=43960000(300) z=43960000(300)

在这里插入图片描述

(4)正常范围的一正一负相加(|正|>|负|)

x = − 0.5 ( B F 000000 ) , y = 1.2 ( 3 F 99999 A ) x=-0.5(BF000000),y=1.2(3F99999A) x=0.5(BF000000),y=1.2(3F99999A)

期望: z = 0.7 z=0.7 z=0.7

实际: z = 3 F 333334 ( 0.70000004768372 ) z=3F333334(0.70000004768372) z=3F333334(0.70000004768372)

在这里插入图片描述

x = − 62.123 ( C 2787 D F 4 ) , y = 100.213 ( 42 C 86 D 0 E ) x=-62.123(C2787DF4),y=100.213(42C86D0E) x=62.123(C2787DF4),y=100.213(42C86D0E)

期望: z = 38.09 z=38.09 z=38.09

实际: z = 42185 C 28 ( 38.089996337891 ) z=42185C28(38.089996337891) z=42185C28(38.089996337891)

在这里插入图片描述

(5)正常范围的一正一负相加(|正|<|负|)

x = 1.7169007646178 E − 20 ( 1 E A 2281 D ) , y = − 1.0842014616272 E − 19 ( 9 F F F F F F 5 ) x=1.7169007646178E-20(1EA2281D),y=-1.0842014616272E-19(9FFFFFF5) x=1.7169007646178E20(1EA2281D),y=1.0842014616272E19(9FFFFFF5)

期望: z = 9.1251138516542 E − 20 z=9.1251138516542E-20 z=9.1251138516542E20

实际: z = 9 F D 775 E E ( − 9.1251140132126 E − 20 ) z=9FD775EE(-9.1251140132126E-20) z=9FD775EE(9.1251140132126E20)
在这里插入图片描述

(6)两个加数相差过大

x = 6.3158350761658 E − 29 ( 10 A 0201 D ) , y = 1.0842014616272 E − 19 ( 1 F F F F F F 5 ) x=6.3158350761658E-29(10A0201D),y=1.0842014616272E-19(1FFFFFF5) x=6.3158350761658E29(10A0201D),y=1.0842014616272E19(1FFFFFF5)

期望: z = 1.0842014616272 E − 19 ( 1 F F F F F F 5 ) z=1.0842014616272E-19(1FFFFFF5) z=1.0842014616272E19(1FFFFFF5)

实际: z = 1 F F F F F F 5 ( 1.0842014616272 E − 19 ) z=1FFFFFF5(1.0842014616272E-19) z=1FFFFFF5(1.0842014616272E19)

在这里插入图片描述

(7)一个加数为零

x = 0 ( 00000000 ) , y = 50.2 ( 4248 C C C C ) x=0(00000000),y=50.2(4248CCCC) x=0(00000000),y=50.2(4248CCCC)

期望: z = 50.2 z=50.2 z=50.2

实际: z = 4248 C C C C ( 50.199996948242 ) z=4248CCCC(50.199996948242) z=4248CCCC(50.199996948242)

在这里插入图片描述

(8)两数相加造成下溢

x = 00800010 , y = 80800001 x=00800010,y=80800001 x=00800010,y=80800001 即x和y的指数部分都是1,x是正数,y是负数,两数相加,必然比x和y都小,从而使得结果小于最小的规格化浮点数造成下溢。

计算出的z=0000001e,但是不符合规格化浮点数的要求,标记overflow=2’b10表示这是一个下溢出的结果。

在这里插入图片描述

(9)两数相加造成上溢

x = 7 F 7 F F F F F , y = h 7 F 7 F F F F F x=7F7FFFFF,y=h7F7FFFFF x=7F7FFFFF,y=h7F7FFFFF即x和y都是最大的规格化浮点数,相加之后一定大于了最大的规格化浮点数,期望最终结果标记了overflow=2’b01表面结果上溢。

可以看到波形图中overflow出现了01交替,这是因为对于x和y的运算执行了许多次。

在这里插入图片描述

(10)加数出现无穷大

x = 7 F 800000 , y = 1 F F F F F F 0 x=7F800000,y=1FFFFFF0 x=7F800000,y=1FFFFFF0即x为无穷大,y为规格化浮点数。期望结果也是无穷大,所以直接让结果z为无穷大即可,并且标记overflow=2’b11表示这是非规格化数字。

在这里插入图片描述

x = 7 F 800000 , y = 00000003 x=7F800000,y=00000003 x=7F800000,y=00000003即x为无穷大,y为非规格化浮点数。显然结果应该是无穷大。所以直接让结果z为无穷大即可,并且标记overflow=2’b11表示这是非规格化数字。

在这里插入图片描述

(11)加数出现非数(NaN)

x = 7 F 800003 , y = 7 F 800004 x=7F800003,y=7F800004 x=7F800003,y=7F800004即x和y都是非规格化浮点数且都是NaN。任意数加NaN的结果都是NaN,直接赋值z=32’hffffffff,并且标记最后的overflow=2’b11,表示这个结果是非规格化数字。

在这里插入图片描述

x = 00000003 , y = h 7 F 800004 x=00000003,y=h7F800004 x=00000003,y=h7F800004即x是非规格化浮点数,y为非数NaN。任意数加NaN的结果都是NaN,直接赋值z=32’hffffffff,并且标记最后的overflow=2’b11,表示这个结果是非规格化数字。

在这里插入图片描述

(12)加数出现非规约形式的浮点数

x = 00000003 , y = 00000005 x=00000003,y=00000005 x=00000003,y=00000005,实际上是正常加法,只是尾数部分不再有隐含1。

期望: z = 00000008 z=00000008 z=00000008

实际: z = 00000008 z=00000008 z=00000008 并且注意这是下溢,所以overflow=2’b10

在这里插入图片描述

x = 00000003 , y = 1.175494631082 E − 38 ( 00800002 ) x=00000003,y=1.175494631082E-38(00800002) x=00000003,y=1.175494631082E38(00800002)

期望:由于x相较于y是很小的,所以结果期望很接近y

实际: z = 00800003 ( 1.1754947712118 E − 38 ) z=00800003(1.1754947712118E-38) z=00800003(1.1754947712118E38)

在这里插入图片描述

五.附录

浮点数加法器代码

module floatadd (
    input clk,
    input rst,
    input [31:0] x,
    input [31:0] y,
    output reg [31:0] z,
    output reg [1:0] overflow//0-没有溢出;1-上溢;10-下溢;11-输入不是规格化数
);
    reg [24:0] m_x,m_y,m_z; 
    reg [7:0] exponent_x,exponent_y,exponent_z;
    reg [2:0] state_now,state_next;
    reg sign_z,sign_x,sign_y;

    reg [24:0] out_x,out_y,mid_y,mid_x;
    reg [7:0] move_tot;
    reg [2:0] bigger;

    parameter start = 3'b000,zerocheck = 3'b001,equalcheck = 3'b010,addm = 3'b011,normal = 3'b100,over = 3'b110;

    always @(posedge clk) begin
        if(!rst)begin
            state_now<=start;
        end
        else begin
            state_now<=state_next;
        end
    end
    
    always @(state_now,state_next,exponent_x,exponent_y,exponent_z,m_x,m_y,m_z,out_x,out_y,mid_x,mid_y) begin
        case(state_now)
            start:begin//分离指数和尾数 
                bigger<=2'b0;
                exponent_x<=x[30:23];
                exponent_y<=y[30:23];
                m_x<={1'b0,1'b1,x[22:0]};//最高位来控制溢出 1.F
                m_y<={1'b0,1'b1,y[22:0]};
                out_x<=25'b0;
                out_y<=25'b0;
                mid_y<={24'b0,1'b1};
                mid_x<={24'b0,1'b1};
                move_tot<=8'b0;
                //只有阶码[1,254]和实数0是规格化数字
                if((exponent_x==8'd255&&m_x[22:0]!=0)||(exponent_y==8'd255&&m_y[22:0]!=0))begin//出现了NaN,任意数+NaN=NaN
                    overflow<=2'b11;    
                    state_next<=over;
                    sign_z<=1'b1;
                    exponent_z<=8'b11111111;
                    m_z<=23'b11111111111111111111111;
                end
                else if((exponent_x==8'd255&&m_x[22:0]==0)||(exponent_y==8'd255&&m_y[22:0]==0))begin//出现了无穷大,结果必然是无穷大
                    overflow<=2'b11;    
                    state_next<=over;
                    sign_z<=1'b0;
                    exponent_z<=8'b11111111;
                    m_z<=23'b0;
                    // z<=32'h7F800000;
                end
                // else if((exponent_x==8'd0&&m_x[22:0]!=23'b0)||(exponent_y==8'd0&&m_y[22:0]!=23'b0))begin
                //     overflow<=2'b11; 
                //     state_next<=over;
                //     sign_z<=1'b1;
                //     exponent_z<=8'b11111111;
                //     m_z<=23'b11111111111111111111111;
                //     z<=32'hFFFFFFFF;//直接将结果赋值为非规约数,然后回到start阶段
                // end
                else begin
                    // if(exponent_x==8'd0&&m_x[22:0]!=23'b0)begin
                    //     m_x<={1'b0,1'b0,x[22:0]};
                    // end
                    // if(exponent_y==8'd0&&m_y[22:0]!=23'b0)begin
                    //     m_y<={1'b0,1'b0,y[22:0]};
                    // end
                    overflow<=2'b0;
                    state_next<=zerocheck;//进入判断0 阶段
                end
            end
            zerocheck:begin//x==0||y==0,则直接进入over
                if(m_x[22:0]==23'b0&&exponent_x==8'b0)begin
                    sign_z<=y[31];
                    exponent_z<=exponent_y;
                    m_z<=m_y;
                    state_next<=over;
                end
                else if(m_y[22:0]==23'b0&&exponent_y==8'b0)begin
                    sign_z<=x[31];
                    exponent_z<=exponent_x;
                    m_z<=m_x;
                    state_next<=over;
                end
                else begin
                    state_next<=equalcheck;//进入 对阶处理阶段
                end
                if(m_x[22:0]!=23'b0&&exponent_x==8'b0)begin
                    m_x<={1'b0,1'b0,x[22:0]};
                end
                if(m_y[22:0]!=23'b0&&exponent_y==8'b0)begin
                    m_y<={1'b0,1'b0,y[22:0]};
                end
            end
            equalcheck:begin//对阶处理 需要在移位的时候进行向偶舍入
                if(exponent_x==exponent_y)begin
                    if(bigger==2'b00)begin
                        state_next<=addm;//指数对齐,进入尾数加阶段
                    end
                    else if(bigger==2'b10)begin
                        if(out_y>mid_y)begin
                            m_y<=m_y+1'b1;
                        end
                        else if(out_y<mid_y)begin
                            m_y<=m_y;
                        end
                        else if(out_y==mid_y)begin
                            if(m_y[0]==1)begin
                                m_y<=m_y+1'b1;
                            end
                            else begin
                                m_y<=m_y;
                            end
                        end    
                        state_next<=addm;
                    end
                    else if(bigger==2'b01)begin
                        if(out_x>mid_x)begin
                            m_x<=m_x+1'b1;
                        end
                        else if(out_x<mid_x)begin
                            m_x<=m_x;
                        end
                        else if(out_x==mid_x)begin
                            if(m_x[0]==1)begin
                                m_x<=m_x+1'b1;
                            end
                            else begin
                                m_x<=m_x;
                            end
                        end     
                        state_next<=addm;
                    end
                end
                else begin
                    if(exponent_x>exponent_y)begin
                        bigger<=2'b01;
                        exponent_y<=exponent_y+1'b1;
                        m_y[23:0]<={1'b0,m_y[23:1]};//指数+1,尾数右移一位
                        out_y[move_tot]<=m_y[0];
                        mid_y={mid_y[23:0],mid_y[24]};
                        move_tot<=move_tot+1'b1;
                        if(m_y==24'b0)begin//如果x和y的节码差距过大,就不需要考虑太小的
                            sign_z<=sign_x;
                            exponent_z<=exponent_x;
                            m_z<=m_x;
                            state_next<=over;
                        end
                        else begin
                            state_next<=equalcheck;
                        end
                    end
                    else begin
                        bigger<=2'b10;
                        exponent_x<=exponent_x+1'b1;
                        m_x[23:0]<={1'b0,m_x[23:1]};//指数+1,尾数右移一位
                        out_x[move_tot]<=m_x[0];
                        mid_x={mid_x[23:0],mid_x[24]};
                        move_tot<=move_tot+1'b1;
                        if(m_x==24'b0)begin//如果x和y的节码差距过大,就不需要考虑太小的
                            sign_z<=sign_y;
                            exponent_z<=exponent_y;
                            m_z<=m_y;
                            state_next<=over;
                        end
                        else begin
                            state_next<=equalcheck;
                        end
                    end
                end
            end
            addm:begin
                if(x[31]^y[31]==1'b0)begin
                    exponent_z<=exponent_x;
                    sign_z<=x[31];
                    m_z<=m_x+m_y;
                    state_next<=normal;
                end
                else begin
                    if(m_x>m_y)begin
                        exponent_z<=exponent_x;
                        sign_z<=x[31];
                        m_z<=m_x-m_y;
                        state_next<=normal;
                    end
                    else if(m_x<m_y)begin
                        exponent_z<=exponent_x;
                        sign_z<=y[31];
                        m_z<=m_y-m_x;
                        state_next<=normal;
                    end
                    else begin
                        exponent_z<=exponent_x;
                        m_z<=23'b0;
                        state_next<=over;//全零没必要规格化
                    end
                end
                // if(m_z[23:0]==24'b0)begin//全零没必要规格化
                //     state_next<=over;
                // end
                // else begin
                //     state_next<=normal;
                // end
            end
            normal:begin
                if(m_z[24]==1'b1)begin
                    // 有进位/借位
                    m_z<={1'b0,m_z[24:1]};
                    exponent_z<=exponent_z+1'b1;
                    state_next<=over;
                end
                else begin
                    if(m_z[23]==1'b0&&exponent_z>=1)begin//规格化处理,0.xxxx转化成1.xxxxx
                        m_z<={m_z[23:0],1'b0};
                        exponent_z<=exponent_z-1'b1;
                        state_next<=normal;
                    end
                    else begin
                        state_next<=over;
                    end
                end
            end
            over:begin
                z={sign_z,exponent_z[7:0],m_z[22:0]};
                //判断溢出:
                //1.大于最大浮点数
                //2.小于最小浮点数(这部分本身是非规约数),直接输出z,标记出它是下溢出
                if(overflow)begin
                    overflow<=overflow;
                    state_next<=start;
                end
                else if(exponent_z==8'd255)begin
                    overflow<=2'b01;
                    state_next<=start;
                end
                else if(exponent_z==8'd0&&m_z[22:0]!=23'b0)begin
                    overflow<=2'b10;
                    state_next<=start;
                end
                else begin
                    overflow<=2'b00;
		            state_next<=start;
                end 
            end
            default:begin
                state_next<=start;
            end
        endcase
    end
endmodule

testbench代码

`timescale 1ns/1ps
module floatadd_tb();
    reg clk,rst;
    reg [31:0] x,y;
    wire [31:0] z;
    wire [1:0] overflow;
    
    floatadd floatadd_test(
        .clk(clk),
        .rst(rst),
        .x(x),
        .y(y),
        .z(z),
        .overflow(overflow)
    );
    always #(10) clk<=~clk;
    initial begin
        clk=0;
        rst=1'b0;
        #20 rst=1'b1;
        #20 x=32'b00111111010001111010111000010100;//0.78
        y=32'b00111111000011001100110011001101;//0.55
        //ans=0.78+0.55=1.33 32'b00111111 10101010 00111101 01110001 3faa3d70
        #1000
        x=32'h4248CCCC;//50.2
        y=32'h3F8CCCCC;//1.1
        //ans=51.3  424d3332
        #1000
        x=32'h10A0201D;//6.3158350761658E-29
        y=32'h1FFFFFF5;//1.0842014616272E-19
        //ans=1.0842014616272E-19两正数相加,由于阶码相差63,所以小数可以忽略,结果与输入大数相等   1FFFFFF5
        #1000
        x=32'h00000000;
        y=32'h4248CCCC;
        //0+50.2=50.2   4248CCCC
        #1000
        x=32'b01000010110010000000000000000000;//100
        y=32'b01000011010010000000000000000000;//200
        //ans=300 32'b01000011 10010110 00000000 00000000   43960000
        #1000
        x=32'hBF000000;//-0.5
        y=32'h3F99999A;//1.2
        //ans=0.7   3f333334
        #1000
        x=32'hC2787DF4;//-62.123
        y=32'h42C86D0E;//100.213
        //ans=38.09     42185c28
        #1000
        x=32'h9EC0001D;//-2.032883758613E-20
        y=32'h9FFFFFF5;//-1.0842014616272E-19
        //ans=-1.2874898213326E-19  a017fffe
        #1000
        x=32'h1EA2281D;//1.7169007646178E-20
        y=32'h9FFFFFF5;//-1.0842014616272E-19
        //ans=-9.1251140132126E-20  9fd775ee
        #1000
        x=32'h9EE2281D;//-2.3945271224212E-20
        y=32'h1FFFFFF5;//1.0842014616272E-19
        // //ans=8.4474876554092E-20    1fc775ee
        #1000
        x=32'h1EE2281F;//2.3945274455386E-20
        y=32'h1FFFFFF0;//1.0842011385097E-19
        //ans=1.32365388306356E-19  201c44fb
        #1000
        x=32'h7F7FFFFF;
        y=32'h7F7FFFFF;//验证上溢出 overflow=2'b01 7FFFFFFF
        #1000
        x=32'h00800010;
        y=32'h80800001;//验证下溢出 0000001E
        #1000
        x=32'h000003FF;
        y=32'h3F8003FF;//非规格+ overflow=2'b11 3f8003ff
        #1000
        x=32'h7F800003;
        y=32'h7F800004;//验证NaN overflow=2'b11 FFFFFFFF
         #1000
        x=32'h00000000;//
        y=32'h9FFFFFF0;//-1.0842011385097E-19   9FFFFFF0
        //验证判断0阶段功能
        #1000
        x=32'h00000003;
        y=32'h00000005;//非规格数字+非规格 数字 00000008
        #1000
        x=32'h1FFFFFFF;//1.084202107862E-19
        y=32'h9FFFFFF0;//-1.0842011385097E-19
        //ans=0.0000009693523   15F00000
        #1000
        x=32'h00000003;
        y=32'h00800002;//非规格数字+正常数字 overflow=2'b11 
        #1000
        x=32'h1EE2281F;//2.3945274455386E-20
        y=32'h1FFFFFF0;//1.0842011385097E-19
        //ans=1.32365388306356E-19 
        #1000
        x=32'h00000003;
        y=32'h7F800004;//非规格数字+正常数字 overflow=2'b11 
        #1000
        x=32'h1EE2281F;//2.3945274455386E-20
        y=32'h1FFFFFF0;//1.0842011385097E-19
        //ans=1.32365388306356E-19 
        #1000
        x=32'h7F800000;
        y=32'h00000003;//验证无穷大,结果为无穷大 overflow=2'b11 
        #1000
        x=32'h1EE2281F;//2.3945274455386E-20
        y=32'h1FFFFFF0;//1.0842011385097E-19
        //ans=1.32365388306356E-19 
        #1000
        x=32'h7F800000;
        y=32'h1FFFFFF0;//验证无穷大,结果为无穷大 overflow=2'b11 
        #2000 $stop;
    end
endmodule
  • 20
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
### 回答1: Verilog是一种硬件描述语言,用于描述和设计数字电路。要实现32位浮点数的加法,我们可以使用Verilog语言编写一个模块,该模块接收两个32位浮点数作为输入,并输出它们的和。 首先,我们需要定义输入和输出端口。我们可以为输入端口定义两个32位的浮点数输入信号a和b,并为输出端口定义一个32位的浮点数输出信号result。 接下来,我们可以使用IEEE 754浮点数标准来表示32位浮点数的结构。在Verilog中,我们可以使用一个32位的向量来表示浮点数的各个部分,例如符号位、指数位和尾数位。 然后,我们可以创建一个组合逻辑来实现浮点数的加法操作。我们可以使用逻辑门和多路选择器来处理各个部分的操作,例如判断符号位、计算指数相加、进行尾数的规格化等。 最后,我们可以将结果赋值给输出信号result,并将其传递给其他组件进行进一步处理或显示。 需要注意的是,该实现仅是一个基本的概念示例,实际的32位浮点数加法可能需要更多的细节和复杂的操作来实现精确的结果。 总之,通过使用Verilog语言,我们可以实现一个模块来执行32位浮点数的加法操作,并将结果输出给其他组件。这样,我们就可以在数字电路中使用该模块来执行浮点数加法运算。 ### 回答2: 要实现32位浮点数加法,可以使用Verilog语言进行设计和编码。 首先,我们需要确定浮点数的数据格式。常见的32位浮点数格式是IEEE 754单精度浮点数格式。该格式使用1位符号位(S)、8位指数位(E)和23位尾数位(M)。 在Verilog中,我们可以使用模块化方法来实现浮点数加法器。首先,我们可以定义一个模块,包含两个32位浮点数输入(input a, b)和一个32位浮点数输出(output out)。 接下来,我们可以将浮点数进行拆分,将尾数和指数分开处理。我们可以使用Verilog中的位切割操作符,将浮点数按照指定的位数进行拆分和连接。 然后,我们需要根据指数的差异进行对齐操作。如果两个浮点数的指数不相同,我们需要将指数较小的浮点数的尾数右移,直到两个指数相等。我们可以使用Verilog中的移位操作符来实现这一步骤。 接下来,我们可以将两个浮点数的尾数进行加法运算。由于尾数是一个二进制小数,我们可以使用Verilog中的加法器进行加法运算。 在加法运算完成后,我们还需要考虑产生的结果是否需要进行规格化。如果尾数的最高位数为1,则表示结果需要进行规格化,即尾数左移一位,并且指数加1。 最后,我们需要根据符号位确定结果的符号,并将结果输出。 以上就是大致的逻辑设计和实现过程。在实际编码过程中,还需要进行测试、验证和调试,以确保实现的正确性和可靠性。 ### 回答3: Verilog是硬件描述语言,可以用于设计电子系统的行为模型和结构模型。要实现32位浮点数加法,需要在Verilog代码中定义适当的输入和输出端口以及内部变量。 首先,我们可以定义一个module,其中包含输入端口A和B,表示要相加的两个32位浮点数,以及一个输出端口C,表示相加的结果。 ```verilog module float_adder( input [31:0] A, input [31:0] B, output [31:0] C ); ``` 接下来,我们需要将输入的32位浮点数解析为符号位、指数位和尾数位。根据IEEE 754单精度浮点数的规范,符号位占1位,指数位占8位,尾数位占23位。 ```verilog reg sign_A, sign_B; reg [7:0] exponent_A, exponent_B; reg [22:0] mantissa_A, mantissa_B; assign sign_A = A[31]; assign sign_B = B[31]; assign exponent_A = A[30:23]; assign exponent_B = B[30:23]; assign mantissa_A = A[22:0]; assign mantissa_B = B[22:0]; ``` 接下来,我们可以实现相应的加法运算,将解析的浮点数值进行相加。 ```verilog reg [31:0] mantissa_sum; reg [7:0] exponent_diff; reg [23:0] mantissa_shift; // 判断两个浮点数的指数差,进行对齐 always @(exponent_A, exponent_B) begin exponent_diff = exponent_A - exponent_B; if(exponent_diff < 0) begin exponent_diff = -exponent_diff; mantissa_shift = {1'b0, mantissa_B}; mantissa_A = mantissa_A << exponent_diff; end else if (exponent_diff > 0) begin mantissa_shift = {1'b0, mantissa_A}; mantissa_B = mantissa_B << exponent_diff; end else begin mantissa_shift = {1'b0, mantissa_B}; end end // 对齐后的尾数相加 always @(mantissa_A, mantissa_shift) begin mantissa_sum = (sign_A === sign_B ? mantissa_A + mantissa_shift : mantissa_A - mantissa_shift); end ``` 最后,我们需要根据加法结果的符号位、指数位和尾数位将结果合并并输出。 ```verilog reg [31:0] exponent_C; reg [31:0] mantissa_C; // 判断加法结果的符号位 always @(sign_A, sign_B, mantissa_sum) begin if(sign_A === sign_B) begin C[31] = sign_A; end else if (mantissa_sum > 0) begin C[31] = sign_A; end else begin C[31] = sign_B; end end // 计算加法结果的指数位和尾数位 always @(exponent_diff, exponent_A, exponent_B, mantissa_sum) begin exponent_C = (exponent_A > exponent_B ? exponent_A : exponent_B); if(mantissa_sum[23]) begin exponent_C = exponent_C + 1; mantissa_C = mantissa_sum >> 1; end else begin mantissa_C = mantissa_sum; end end assign C[30:23] = exponent_C; assign C[22:0] = mantissa_C; endmodule ``` 这样,我们就用Verilog实现了32位浮点数加法。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Phoenix_ZengHao

创作不易,能否打赏一瓶饮料?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值