文章目录
- (基础)半精度浮点数的表示和乘运算
- 16位半精度浮点数
- 浮点数的乘运算
- floatMult16完整代码
- floatMult16代码逐步解析
- 符号位sign判断
- 指数exponent计算
- 尾数fraction计算
- 尾数fraction的标准化和舍位
- 整合为最后的16位浮点数结果[sign,exponent,fraction]
- 其他
- 变量宽度表
- always敏感列表
- 特殊情况处理
(基础)半精度浮点数的表示和乘运算
16位半精度浮点数
浮点数的乘运算
根本原理式:
X = X S ⋅ 2 X E Y = Y S ⋅ 2 Y E X ⋅ Y = ( X S ∗ Y S ) ⋅ 2 X E + Y E X = X_{S} · 2^{X_{E}} \\ Y = Y_{S} · 2^{Y_{E}} \\ X·Y = (X_{S}*Y_{S}) · 2^{X_{E} + Y_{E}} X=XS⋅2XEY=YS⋅2YEX⋅Y=(XS∗YS)⋅2XE+YE
基本流程:
-
判符号。
-
算尾数:结果的尾数是输入两数的尾数之积。(未标准化)
-
算指数:结果的指数是输入两数的指数之和。(未标准化)
-
标准化和舍位:
① 结果化为二级制(1.xx)的形式,取出尾数。
② 舍去低位,保留高位。
floatMult16完整代码
完整代码:
`timescale 100 ns / 10 psmodule floatMult16 (floatA,floatB,product);input [15:0] floatA, floatB;
output reg [15:0] product;reg sign;
reg signed [5:0] exponent; //6th bit is the sign
reg [9:0] mantissa;
reg [10:0] fractionA, fractionB; //fraction = {1,mantissa}
reg [21:0] fraction;always @ (floatA or floatB) beginif (floatA == 0 || floatB == 0) beginproduct = 0;end else beginsign = floatA[15] ^ floatB[15];exponent = floatA[14:10] + floatB[14:10] - 5'd15 + 5'd2;fractionA = {1'b1,floatA[9:0]};fractionB = {1'b1,floatB[9:0]};fraction = fractionA * fractionB;if (fraction[21] == 1'b1) beginfraction = fraction << 1;exponent = exponent - 1; end else if (fraction[20] == 1'b1) beginfraction = fraction << 2;exponent = exponent - 2;end else if (fraction[19] == 1'b1) beginfraction = fraction << 3;exponent = exponent - 3;end else if (fraction[18] == 1'b1) beginfraction = fraction << 4;exponent = exponent - 4;end else if (fraction[17] == 1'b1) beginfraction = fraction << 5;exponent = exponent - 5;end else if (fraction[16] == 1'b1) beginfraction = fraction << 6;exponent = exponent - 6;end else if (fraction[15] == 1'b1) beginfraction = fraction << 7;exponent = exponent - 7;end else if (fraction[14] == 1'b1) beginfraction = fraction << 8;exponent = exponent - 8;end else if (fraction[13] == 1'b1) beginfraction = fraction << 9;exponent = exponent - 9;end else if (fraction[12] == 1'b0) beginfraction = fraction << 10;exponent = exponent - 10;end mantissa = fraction[21:12];if(exponent[5]==1'b1) begin //exponent is negativeproduct=16'b0000000000000000;endelse beginproduct = {sign,exponent[4:0],mantissa};endend
endendmodule
floatMult16代码逐步解析
符号位sign判断
正正得正,负负得正,正负得负。用异或运算即可。
sign = floatA[15] ^ floatB[15];
指数exponent计算
floatA与floatB的指数相加,初步得到了乘法结果的指数(尚未标准化)。
- 后面进行标准化时,指数要随着"尾数小数点"的移动而变化。
exponent = floatA[14:10] + floatB[14:10] - 5'd15 + 5'd2;
尾数fraction计算
floatA与floatB的尾数相乘,初步得到了乘法结果的尾数(尚未标准化)。
- 输入的两个浮点数是上一级操作完成的。fraction为省略整数1的小数部分,因此运算时需要先把这个整数1还回去。
- 此步得到的运算结果尚未经历标准化。
- 两个10bit的二进制相乘结果先暂存为20bit,避免在乘法过程中造成精度损失。后续再进行移位和舍位。
//输入的两个浮点数A,B都是标准的。把整数1先借回去,以便参与运算。
fractionA = {1'b1,floatA[9:0]};
fractionB = {1'b1,floatB[9:0]};
//尾数相乘
fraction = fractionA * fractionB;
尾数fraction的标准化和舍位
通过小数点的移动,将运算结果变为二进制(1.xx)的形式。尾数即取小数点后(xx)的部分。同时,在这个过程中"指数"要同步发生变化。
- 尾数小数点"左移n位",二进制右移:指数+n。
- 尾数小数点"右移n位",二进制左移:指数-n。
代码的思路是:
-
从乘法结果尾数的高位开始,寻找到最高位的1。通过左移,将"这一位"变为最高位。
-
半精度浮点数尾数位只取5位。舍去低位。
标准化:
if (fraction[21] == 1'b1) beginfraction = fraction << 1;exponent = exponent - 1; end else if (fraction[20] == 1'b1) beginfraction = fraction << 2;exponent = exponent - 2;end else if (fraction[19] == 1'b1) beginfraction = fraction << 3;exponent = exponent - 3;end else if (fraction[18] == 1'b1) beginfraction = fraction << 4;exponent = exponent - 4;end else if (fraction[17] == 1'b1) beginfraction = fraction << 5;exponent = exponent - 5;end else if (fraction[16] == 1'b1) beginfraction = fraction << 6;exponent = exponent - 6;end else if (fraction[15] == 1'b1) beginfraction = fraction << 7;exponent = exponent - 7;end else if (fraction[14] == 1'b1) beginfraction = fraction << 8;exponent = exponent - 8;end else if (fraction[13] == 1'b1) beginfraction = fraction << 9;exponent = exponent - 9;end else if (fraction[12] == 1'b0) beginfraction = fraction << 10;exponent = exponent - 10;end
舍位:
mantissa = fraction[21:12];
整合为最后的16位浮点数结果[sign,exponent,fraction]
使用"拼接"语法,最后结果product为16位。
product = {sign,exponent[4:0],mantissa};
其他
变量宽度表
input [15:0] floatA, floatB;
output reg [15:0] product;reg sign;
reg signed [5:0] exponent;
reg [9:0] mantissa;
reg [10:0] fractionA, fractionB;
reg [21:0] fraction;
always敏感列表
本模块为纯组合逻辑,非时序。因此只要有数据输入,便触发模块功能,开始运算。
always @ (floatA or floatB) begin/* -------------------- */
end
特殊情况处理
- 输入两个数都是0时,输出也为0。
if (floatA == 0 || floatB == 0) beginproduct = 0;
end
- 不允许指数为负数。在半精度浮点数中,指数本来只有5位。但是代码中的变量设置为6位,其中多出的最高一位是符号位。通过这一位,我们来判断指数的正负。
(这一点从上面操作fraction只进行左移也可以看出来。)
if(exponent[5]==1'b1) begin product=16'b0000000000000000;
end
学习文章:二进制浮点数以及二进制浮点数算术运算
开源项目github-URL:CNN-FPGA