32位浮点数据格式如下:
S:符号位 1Bit
Exp:阶码位 7Bits
Fra:尾码位 24Bits
相对于浮点乘法器来说,加法器的实现逻辑稍微复杂一点
32 位浮点数据格式: A = (- 1) S ×M ×2E-127。其中加法运算操作分为:
(1) 对阶,使两数的小数点位置对齐。两个浮点数进行运算首先要看阶码是否相同,也就是看小数点有没有对齐;
用十进制来举例,10.32和1.3612相加对计算机而言不能直接对齐小数点,计算机会按照数值的位数对应进行加减法,所以得出的数值是14644,而小数点的位置则会未知;
对阶码首先要求出两阶码的差:Ex - Ey,在对阶时总是小阶向大阶看齐,也就是小数点向左移,比较小的阶码则会变大,直到与比较大的阶码相等为止。
(2) 尾数求和,将对阶后的两尾数按定点加减运算规则求和(差)
(3) 规格化,为增加有效数字的位数,提高运算精度,必须将求和 (差)后的尾数规格化。在浮点加减运算时,尾数求和的是01.xxxxxx或者10.xxxxxxxx在定点加减法运算中称为溢出,但是在浮点加减法运算中,这表示运算结果的绝对值大于1,这时通过将尾数右移实现规格化,称为向右规格化,尾数向右移一位,阶码加1;当尾数不是1.M的形式时,向左规格化,也就是尾数向左移。
(4) 舍入,为提高精度,要考虑尾数右移时丢失的数值位。在对阶或向右规格化结束后,尾数向右移则需要丢掉尾数的低位部分,所以要进行舍入处理。
这里有四种舍入处理的方法:
a.就近舍入:通常所说的“四舍五入”,多余位的值超过规定的最低有效位值的一半,最低有效位值加1.
例如: IEEE 32位标准化格式尾数位为23位,假设超出23位的多余位数字是10010,它的十进制值为18,最低有效位值是100000,换算成十进制值是32,18大于16,所以最低有效位值需要加1.
b.朝0舍入:直接截尾;截尾导致尾数取值比起原值的绝对值小,所以这种方法容易导致误差累积。
c.朝+∞舍入:对于正数来说,只要多余位不全为0,则向最低有效位进1;对于负数来说则是直接截尾。
d.朝-∞舍入:对于负数来说,多余位不全为0则向最低有效位进1;对于正数来说则是直接截尾。
(5) 溢出判断, 即判断结果是否溢出。浮点数的溢出由其阶码表现出来的,如果阶码正常,则浮点加减法正常进行;如果阶码溢出,则需要对解码进行处理,并且对尾数也进行处理。
a.阶码上溢:超过了阶码可以表示的最大值的正数指数,将其看作是∞。
b.阶码下溢:超过了阶码可以表示的最小值的负数指数,将其看作是0。
c.尾数上溢:两个操作数相加产生最高位进1,这时需要将小数点向左移1位,阶码值加1 来重新对齐阶码。
d.尾数下溢:在尾数右移以后,也就是小数点左移之后,尾数的最低有效位应该流出,也就是算入多余位里,要进行舍入处理。
贴Chisel生成的Verilog代码:
module float_add(
input clock,
input reset,
input [31:0] io_a,
input [31:0] io_b,
input io_in_en,
output [31:0] io_c,
output io_out_en
);
reg [25:0] fra_min; // @[float_add.scala 21:26]
reg [31:0] _RAND_0;
reg [25:0] fra_max; // @[float_add.scala 22:26]
reg [31:0] _RAND_1;
reg [8:0] exp_max; // @[float_add.scala 23:26]
reg [31:0] _RAND_2;
reg [25:0] fra_add; // @[float_add.scala 24:26]
reg [31:0] _RAND_3;
reg [8:0] exp_maxr; // @[float_add.scala 25:30]
reg [31:0] _RAND_4;
reg [22:0] fra; // @[float_add.scala 28:26]
reg [31:0] _RAND_5;
reg [7:0] exp; // @[float_add.scala 29:26]
reg [31:0] _RAND_6;
reg sign; // @[float_add.scala 30:26]
reg [31:0] _RAND_7;
reg _T; // @[Reg.scala 15:16]
reg [31:0] _RAND_8;
reg _T_1; // @[Reg.scala 15:16]
reg