reference:
https://blog.csdn.net/niaolianjiulin/article/details/82764511
https://blog.csdn.net/baidu_24281959/article/details/52015811
而定点与浮点在《计算机组成原理》有很详细的说明,但是电子专业的学生只学《单片机》,而《单片机》并没有介绍定点与浮点的内容,所以电子专业的学生缺了这点基础,导致在FPGA处理运算时遇到拦路虎。
定点
定点数是非常简单的,它最早在小学的时候就已经学了,只不过那个时候并没有使用定点这个术语。
如上图所示,31.5米=315分米,显然根据单位的不同,小数点是可以移动的。只要单位固定了,那么小数点也固定,所以叫定点数。那么二进制的定点数,也是同样的原理。
浮点
由于定点数所表示数的范围非常有限,所以才弄了浮点数,以扩充数的范围,以便给精度要求高的场合下使用。于是,得到IEEE754标准的格式,如下图所示。
虽然在CPU内部运算时使用,但是在显示给人看的时候,根据实际需求,可以转成10的e次方等形式。
浮点数在运算过程中,需要对阶(也就是让指数位相同),而对阶过程中就常常使小数点移动,所以叫浮点数。
使用浮点数格式处理加、减、乘、除等运算,有两种方法,一种是使用定点模拟浮点,另一种是使用FPU(浮点处理单元)。
定点模拟浮点,运算速度较慢,在一些没有FPU的CPU会用得上(如51单片机)。
FPU是在CPU内部集成的外设,运算速度较快,但是CPU的成本会增加(如STM32F4系列)。
然而大多数FPGA内部没有集成FPU,要么使用IP核(OpenCores有),要么自己用HDL写一个。
浮点:小数点非固定的数,可表示数据范围较广,整数,小数都可表示。包含float,double;
定点:小数点固定,可表示整数,小数。int本质是小数点位于末尾的32位定点数而已;
IEEE 754标准
规定浮点数格式为:- s表示符号位,当s=0,V为正数;当s=1,V为负数
- m表示尾数
- n表示阶码
例如,前面绘出的浮点数的表示形式中,s=0,n=132,m=(1/2+0/4+0/8+1/16+0/32+……),则计算结果为50.0
定点数的表示法
对于计算机来说,浮点定点的概念是看不见的,因为它只能看到:0…00001110,至于它表示多少,是逻辑层面的设置。你如果让它是int那就按照int表示法对每个位赋予意义,如果你让它是float就按照float表示法赋予意义。
对于00011100表示的定点数:
- 如果我们设定小数点是位于最后一位的,即00011100. 则其表示28
- 若设定小数点位于后三位的,即00011.100 则其表示3.50
- 若设定小数点位于后四位的,即0001.1100 则其表示1.75
可以看到:
- 小数位数越多,表示的精度越高。若小数点后有n位,则其表示的最大精度为1/(2n);
- 整数位数越多,可表示的最大值越大。
以8位为例,最高位为符号位:
- 若整数位占4位,小数位占3位,则其最大精度为0.125,最大值为15.875
- 若整数位占5位,小数位占2位,则其最大精度为0.250,最大值为31.750
- 若整数位占6位,小数位占1位,则其最大精度为0.500,最大值为63.500
- 若整数位占7位,小数位占0位,则其最大精度为1.000,最大值为127
为何要把浮点数转换为定点数呢?
1.这来源于项目中神经网络的需求,网络中大量的参数,如果全部用F32表示,一是占用空间大,二是读取效率不高。
如果我们可以将某些浮点数转换为定点数表示,在接受精度损失的前提下,每次就可以读取多个进行运行,可显著提高运算效率;
2.浮点数在运算过程中,需要对阶(也就是让指数位相同),而对阶过程中就常常使小数点移动;
3.FPU是在CPU内部集成的外设
举例来说,我们用8位定点数,1个符号位,4个整数位,3个小数位,则其可表示范围是-16.00~15.875,最大精度0.125。
有几个浮点数:0.145,1.231,2.364,7.512,每个需要32bit表示。
如果我们将每个量化成一个8位定点数,比如通过某种方法得到:1,10,19,60
此时每个数需要8bit表示。那么读一个浮点数,可以同时读4个定点数,且计算效率可以提高。当然这样做是有风险的:
- 损失精度,比如再将上述定点数转化为浮点数:0.125,1.250, 2.375,7.500;
- 定点数表示范围有限,加法有可能会溢出,需要拿int16或int32来暂存中间结果;
如何将浮点数转换为定点数?
我们用8位定点数,1个符号位,4个整数位,3个小数位。这个3称为量化系数。该过程称为量化。
(我们总是将非离散值量化到离散值空间,处理更为简单)
如何将定点数转换为浮点数
该过程称为反量化。
还有个note:
- 定点数加减时需要量化系数相同,其值有可能溢出,需要更大定点数来暂存中间值;
- 两个定点数乘法后如果需要转化为f32,则反量化系数变为2∗n
16位量化
参与数值运算的数为16位的整型数,通过设定小数点在16位数中的不同位置,就可以表示不同大小和不同精度的小数。
Q表示 | S表示 | 十进制数表示范围 |
---|---|---|
Q15 | S0.15 | -1≤x≤0.9999695 |
Q14 | S1.14 | -2≤x≤1.9999390 |
Q13 | S2.13 | -4≤x≤3.9998779 |
Q12 | S3.12 | -8≤x≤7.9997559 |
Q11 | S4.11 | -16≤x≤15.9995117 |
Q10 | S5.10 | -32≤x≤31.9990234 |
Q9 | S6.9 | -64≤x≤63.9980469 |
Q8 | S7.8 | -128≤x≤127.9960938 |
Q7 | S8.7 | -256≤x≤255.9921875 |
Q6 | S9.6 | -512≤x≤511.9804375 |
Q5 | S10.5 | -1024≤x≤1023.96875 |
Q4 | S11.4 | -2048≤x≤2047.9375 |
Q3 | S12.3 | -4096≤x≤4095.875 |
Q2 | S13.2 | -8192≤x≤8191.75 |
Q1 | S14.1 | -16384≤x≤16383.5 |
Q0 | S15.0 | -32768≤x≤32767 |
不同的Q所表示的数不仅范围不同,而且精度也不相同。
Q越大,数值范围越小,但精度越高;相反,Q越小,数值范围越大,但精度就越低。
转换关系
浮点数与定点数的转换关系可表示为:
浮点数(Fx)转换为定点数(Ix):Ix = (int)x* 2^Q
定点数(Ix)转换为浮点数(Fx):Fx= (float)Ix*2^(-Q)
转换示例:
浮点数 Fx = 0.5,定标 Q = 15,则定点数:
Ix = floor(0.5*32768) = 16384
反之,一个用 Q = 15 表示的定点数Ix = 16384,其浮点数为:
Fx = 16384 * 2^(-15) = 16384 / 32768 = 0.5
浮点数转换为定点数时,为了降低截尾误差,可以在取整前可以先加上0.5,视情况而定。
定点加减
将浮点加法/减法转化为定点加法/减法时最重要的一点就是必须保证两个操作数的定标:
若两者不一样,则在做加法/减法运算前先进行小数点的调整。
为保证运算精度,需使Q值小的数调整为与另一个数的Q值一样大。
此外,在做加法/减法运算时,必须注意结果可能会超过16位表示。
如果加法/减法的结果超出16位的表示范围,则必须保留32位结果,以保证运算的精度,否则可能会出现严重的精度丢失。
# 设x的Q值为Qx,y的Q值为Qy,且Qx > Qy,加法/减法结果z的定标值为Qz
# 所以定点加法可以描述为:
int16 x,y;
#结果用更大长度的存
int z;
int temp; // 临时变量
temp = y << (Qx - Qy);
# Q大-Q小,Q大的变量分辨率更高,Q小的左移增加其Q
temp = x + temp;
z = (temp >> (Qx - Qz)); // if Qx >= Qz
z = (temp << (Qz - Qx)); // if Qx <= Qz
// 设x = 0.5,y = 3.1,则浮点运算结果为z = x+y = 0.5+3.1 = 3.6;
// Qx = 15,Qy = 13,Qz = 13,则定点加法为:
x = 16384;y = 25395;
temp = 25395 << 2 = 101580;
temp = x+temp = 16384+101580 = 117964;
z = (int)(117964L >> 2) = 29491;
#因为z的Q值为13,所以定点值z = 29491即为浮点值z = 29491/8192 = 3.5999755859375
自己理解的版本:因为计算过程中并不知道实际的Qz,因此上个版本实际上不可行,因此有:
# 设x的Q值为Qx,y的Q值为Qy,且Qx > Qy,加法/减法结果z的定标值为Qz
# 所以定点加法可以描述为:
int16 x,y;
#结果用更大长度的存
int z;
int temp; // 临时变量
temp = y << (Qx - Qy);
# Q大-Q小,Q大的变量分辨率更高,Q小的左移增加其Q
z= x + temp;
# 设x = 0.5,y = 3.1,则浮点运算结果为z = x+y = 0.5+3.1 = 3.6
# Qx = 15,Qy = 13,Qz = 13,则定点加法为:
x = 16384;y = 25395;
temp = 25395 << 2 = 101580;
temp = x+temp = 16384+101580 = 117964;
z = (int)(temp) = 117964;
#因为x的Q值为15,所以定点值z = 117964即为浮点值z = 117964/32768= 3.5999755859375,与上一版相同
定点乘法
int x,y,z;
long temp;
temp = (long)x;
z = (temp×y) >>(Qx+Qy-Qz);
# 设x = 18.4,y = 36.8,则浮点运算值为z = 18.4×36.8 = 677.12;
# 根据上节,得Qx = 10,Qy = 9,Qz = 5,所以
x = 18841;y = 18841;
temp = 18841L; // Long int
z = (18841L * 18841) >> (10+9-5) = 354983281L >> 14 = 21666;
# 因为z的定标值为5,故定点 z = 21666即为浮点的 z = 21666/32 = 677.0768756866455078125 产生了精度损失
自己理解的版本:因为计算过程中并不知道实际的Qz,因此上个版本实际上不可行,因此有:
int16 x,y,
#结果用更大长度的存
int z;
int temp;
temp = (int)x;
z = x*y
# 设x = 18.4,y = 36.8,则浮点运算值为z = 18.4×36.8 = 677.12;
# 根据上节,得Qx = 10,Qy = 9,所以
x = 18841;y = 18841;
z=18841*18841=354983281
#浮点z = 354983281/(2^10+2^9) =354983281/(524288)= 677.0768756866455078125 产生了精度损失