浮点数和定点数

最近在研究CNN的硬件实现,其中涉及到特征图像、权重和偏移文件的量化,需要研究浮点数、定点数之间的转化。

一.浮点数

C语言中有3种浮点数,float型、double型和long double型,其中float型占4个字节,double型占8个字节,longdouble型长度要大于等于double型,本文档将以float型为例进行介绍,double型和long double型只是比float型位数长,原理都是一样的。

float型可以表示的范围是-3.402823466e38-3.402823466e38,而作为同为4个字节的定点数却只能表示-2147483648~2147483647的范围,使用同样的内存空间,浮点数却能比定点数表示大得多的范围,这是不是太神奇了?既然浮点数能表示这么大的范围,那么我们为何不使用浮点数来代替定点数呢?

先不说浮点数实现起来比较复杂,有些处理器还专门配置了硬件浮点运算单元用于浮点运算,主要原因是浮点数根本就无法取代定点数,因为精度问题。鱼和熊掌不可兼得,浮点数表示了非常大的范围,但它失去了非常准的精度。在说明精度问题前,我们先了解一下浮点数的格式。

ANSI/IEEEStd 754-1985标准
IEEE 754是最广泛使用的二进制浮点数算术标准,被许多CPU与浮点运算器所采用。IEEE754规定了多种表示浮点数值的方式,在本文档里只介绍32bits的float浮点类型。它被分为3个部分,分别是符号位S(sign bit)、指数偏差E(exponent bias)和尾数位F(fraction)。
在这里插入图片描述
单精度浮点数数据位宽共有 32 位,可以分为三个部分,其中符号位 S 只有 1 位,指数位E 为 8 位,尾数位 F 为 23 位,其代数形式为:


在这里插入图片描述
若一个浮点数为0x41040000,在计算机中存储的形式为0100 0001 0000 0100 0000 0000 0000 0000b,

符号位为:0

指数位为:1000 0010b = 130 130-127=3

尾数位为:000 0100 0000 0000 0000 0000
1.000 0100 0000 0000 0000 0000 = 1+(1/2^5) = 1.03125
1.03125*2^2 = 8.25
因此,该数据表示的浮点数为:8.25

二、定点数

参与数值运算的数为16位的整型数。但在许多情况下,数学运算过程中的数不一定都是整数。

应该说,运算芯片本身无法处理小数。关键就是由程序员来确定一个数的小数点处于16位中的哪一位。这就是数的定标。

{通过设定小数点在16位数中的不同位置,就可以表示不同大小和不同精度的小数}

数的定标有Q表示法和S表示法两种。下面列出了一个16位数的16种Q表示、S表示及它们所能表示的十进制数值范围:

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

2.1 定点表示示例:

同样一个16位数,若小数点设定的位置不同,它所表示的数也不同(首位为符号位):

16进制数2000 H= 二进制数0 010 0000 0000 0000 B= 十进制数8192, Q0表示法
16进制数 2000 H= 二进制数0 010 0000 0000 0000 B= 十进制数0.25 , Q15表示法

三、 浮点定点转换:

不同的Q所表示的数不仅范围不同,而且精度也不相同。

Q越大,数值范围越小,但精度越高;相反,Q越小,数值范围越大,但精度就越低。

E.g.

Q0 的数值范围是-32768到+32767,其精度为1;而Q15的数值范围为-1到0.9999695,精度为1/32768=0.00003051。

因此,对定点数而言,数值范围与精度是一对矛盾。

一个变量要想能够表示比较大的数值范围,必须以牺牲精度为代价;而想精度提高,则数的表示范围就相应地减小。

在实际的定点算法中,为了达到最佳的性能,必须充分考虑到这一点。

3.2 转换关系:

浮点数与定点数的转换关系可表示为:

浮点数(Fx)转换为定点数(Ix):Ix = (int)x* 2^Q
定点数(Ix)转换为浮点数(Fx):Fx= (float)Ix*2^(-Q)

3.3 转换示例:

浮点数 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,视情况而定。

四、程序验证

#define _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main()
{
	float value;
	float *var = &value;
	int value_int;
	int *var_int = &value_int;

	scanf("%f", var);
	scanf("%d", var_int);

	printf("%x\n", *((int*)var));//%x 16进制输出整数
	printf("%x\n", *var_int);
	
	return 0;

}

结果如下:


在这里插入图片描述

可见,浮点数在计算机中的保存、数据处理是按照浮点数标准进行的,int,short等整形数据类型是按照二进制补码来表示的。

浮点数转定点数的程序如下:
Qn为浮点数定标;

short float2fixed_fun(float fdata, int Qn){
	short sdata;
	int temp;
	int integer = 2 << (Qn - 1);

	if (fdata > 0){
		temp = int((fdata * integer));
		sdata = (temp == short(temp)) ? temp : (temp >> 31) ^ 0x7fff;
	}
	else if (fdata < 0){
		fdata = -fdata;
		temp = int((fdata * integer));
		sdata = (temp == short(temp)) ? temp : (temp >> 31) ^ 0x7fff;
		sdata = sdata ^ SIGN_BIT;
	}
	else
		sdata = 0;
	return sdata;
}

定点数转浮点数程序如下:

float fixed2float_fun(short sdata, int Qn){
	int sign_flag = sdata & SIGN_BIT;//1->负数 0->正数
	float fdata,temp;
	short temp1;
	int integer = 2 << (Qn - 1);

	if(sign_flag == 0){ //该定点数为正
		fdata = float(sdata)/integer;
	}
	else{
		temp1 = sdata ^ SIGN_BIT;		
		//temp = float(sdata ^ SIGN_BIT);直接使用此语句,结果不正确,不知为何
		temp = float(temp1);
		fdata = temp / integer;
		fdata = -fdata;
	}
	return fdata;

}

C语言小白,从研究浮点数定点数到写好程序用了四天,发现网上的介绍很多,但是开源的代码不多,就把自己写的开源吧,包括浮点定点转换,定点乘法、加法运算等。供参考。
github源码地址:https://github.com/alangaixiaoxiao/CNN-float-fixed-translation-

发布了23 篇原创文章 · 获赞 34 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/alangaixiaoxiao/article/details/104274588