基于FPGA的数字信号处理,一文看懂什么是定点数、浮点数、浮点的定点化、定点的浮点化。


前言

前段时间面试过程中提到了FIR滤波器这个IP核,面试官问FIR滤波器的数据输入输出类型,这里涉及到了我的知识盲区,补一下。
在实际的工程应用中,往往会进行大量的数学运算。运算时除了会用到整数,很多时候也会用到小数。而我们知道在数字电路底层,只有高电平1和低电平0的存在,那么仅凭 0和1 该如何表示小数呢?

数字电路中,小数可以用两种形式来表示:定点数和浮点数。

一、什么是定点数?

「定点数」是英语「fixed-point number」的中文翻译,fixed的意思是固定的,point的意思是小数点,所以「定点数」其实也可以叫「固定小数点的数」。同样的,「浮点数」自然就是「浮动小数点的数」。

在10进制中,小数的表示是通过小数点和它所在位置来实现的。比如12.5,它表示的值是十二点五;而1.25则是一点二五。尽管12.5和1.25都用了「1 2 5」这3个数来表示,但由于小数点位置的不同,使得前者的数值是后者的十倍。

遗憾的是,电路只能表示1和0,无法直接表示小数点,所以上面的方法在电路中是行不通的。不过尽管我们无法直接用小数点来表示2进制小数,但可以通过指定小数点的位置来说明这是一个小数。

约定小数点的位置,且这个位置固定不变,小数点前、后的数字,分别用2进制表示,组合起来就可以用来表示和使用2进制小数了。用这种方式表示的数就叫做「定点数」。

定点数的表示可以分为三种情况:

1、纯整数
这种情况约定小数点在最右边。例如10进制数85用8位无符号2进制数表示就是01010101,因为小数点在最右边,所以可以看做01010101.0。

2、纯小数
定点纯小数是指整数部分为0的小数。根据是无符号数还是有符号数,分为两种情况:
(1)无符号数
无符号数的最高位不表示符号,仅表示数值。
这种情况约定小数点的位置在最左边。例如10进制数0.125用8位无符号2进制数表示就是0.0010_0000,因为小数点在最左边,所以是0010_0000。
(2)有符号数
有符号数的最高位表示符号,不表示数值。
这种情况约定小数点的位置在次高位。例如10进制数-0.125用8位无符号2进制数表示就是1.0010_000(因为二进制系统是基于2的,每个位置的值是2的幂。(0.001)是 2的-3次方=0.125,因此,-0.125的二进制表示为1.0010),因为小数点在次高位,所以是1010_0000。

3、整数 + 小数
除了纯整数和纯小数这两种情况外,其实定点数主要是用来表示 「整数 + 小数」的情况,例如3.14、1.5、25.125等等。这种情况需要确定以下信息才能正确表示该数:
整数部分长度、小数部分长度、是否有符号位。

若以m表示定点数的整数位宽(m不包含符号位),以n表示定点数的小数位宽,则有符号数和无符号数的定点数的表示范围为:
有符号数 -2^m ~ (2 ^m - 2 ^-n)
无符号数 0 ~ (2^m - 2 ^-n)
如果想表示更大范围、更高精度的值,怎么办?

扩大整体位宽:比如使用 16位、32位来表示, 这样相应地整数部分和小数部分的宽度都可以增加,自然表示范围也就变大了。但是位宽的增加,也会带来更多的硬件开销
改变小数点的位置:小数点向后移动,那么整个数字范围就会扩大,但是小数部分的精度就会越来越低。小数点向前移,表示的精度会变高,但是数字的表示范围又会降低。所以说定点数小数点位置的确定是一个范围和精度的trade off(权衡)。

一些定点数的表示格式

除了可以用类似「以5 位表示整数部分,3 位表示小数部分的无符号定点数」的语言来描述定点数的格式外,还有多种定点数的表示格式。常见的有以下几种:

Q格式,Q格式的一般形式是:
Qm.n
默认情况下,用Q格式描述的都是有符号定点数。其中m表示整数部分的长度(这个值不包括符号位),n表示小数部分的长度。所以用Q格式描述的定点数的整体长度为:

w = m + n + 1 //整数部分长度 + 小数部分长度 + 符号位长度

例如 「 Q3.12 」描述的是一个整体长度为16位的2进制有符号定点数,它的整数部分长度是3,而小数部分长度是12。根据小数部分的长度,可以推断出分辨率为 2^-12。

在Q前面加一个 U 即可用来表示无符号的2进制定点数。例如 「 UQ1.15」描述的就是一个整数部分长度为1,小数部分长度为15的无符号的2进制定点数。

整数部分的长度值和小数点可以被省略,而只描述小数部分的长度。例如「 Q12 」描述的就是一个小数部分长度是12的2进制有符号定点数,但是它的整体长度是不确定的,你可以额外指定整体长度,或者说整体长度就取决于存储这个定点数的寄存器。

如果这个定点数存储一个16位的寄存器,那它的值就是:

xxxx . xxxx_xxxx_xxxx

如果这个定点数存储一个32位的寄存器,那它的值就是:

xxxx_xxxx_xxxx_xxxx_xxxx_xxxx . xxxx_xxxx_xxxx

其实也可以看出来,这种表示方法就是把它的值乘以 2^-12 。

S表示法,S表示法的一般形式是:Sm.n
其中S表示这是一个有符号的定点数,m表示整数位数(m不包括符号位),n表示小数位数。这种方法其实跟Q格式很像,只不过它的表示无符号定点数的方法不是在前面加 U ,而是去掉 S,像这样:m.n

例如「 2.4 」表示的就是一个整数位数为2,小数位数为4的无符号定点数。

小结

总的来说,用定点数表示的小数,不仅数值的范围表示有限,而且其精度也很低。要想解决这 2 个问题,人们就提出了使用「浮点数」的方式表示数字。

定点数和浮点数都可以表示小数,而定点数的精度固定,表现范围比较有限;但是,定点数在硬件上比较容易实现,在实际的数据算法中,定点数运算效率比浮点数的运算效率高很多,同时定点数使用的资源也比较少。因此,定点数被广泛地应用在数字信号处理的各种应用场景中。

定点数是在计算机中表示数字的一种方式,它既可以表示整数,也可以表示小数。
在固定 bit 下,约定小数点的位置,然后把整数部分和小数部分分别转换为二进制,就是定点数的结果。
受限于小数点的位置,用定点数表示小数时,数值的范围和小数精度是有限的。
在现代计算机中,定点数通常用来表示整数,对于高精度的小数,通常用浮点数表示。

二、什么是浮点数?

「浮点数」一词,来自英文「float point number」,即「浮动小数点的数」。和科学计数法类似,它们的小数点位置都是浮动的。

和10进制的科学计数法一样,2进制数也可以表示成类似的形式,例如:

101.875(D) = 1100101.111(B) = 1.100101111 * 2^6 = 1100.101111 * 2^3

==所以只需要约定好一定的位数来表示小数部分,一定的位数来表示指数部分,就可以完整地表示一个二进制数。==如何定义这些细节?

IEEE 754 规定了两种常用的浮点数格式:
单精度型,也叫32位型,或者float
双精度型,也叫64位型,或者double
因为这两种格式的表示规则是类似的,只是位宽不一样,了解了其中一种后,就可以快速掌握另一种,所以下文主要介绍 float 类型的浮点数表示方法。

float类型

float 占用 32 位的存储空间,32 位被分为了如下的三个部分:

符号位s:sign,符号位为 0 说明该浮点数为正数,若为 1 则说明浮点数为负数
阶码E:exponent,代表该浮点数被二进制科学表示法规范化后的指数,阶码采用移码表示
尾数M:mantissa,被二进制规约化后要求小数点前一位数必须为 1,所以尾数中实际隐含了最高位 1,例如尾数为 M,则实际在还原时,相当于是 1.M

阶码是用来表示范围的,float定义了8位数的阶码。标准是这样规定的:==阶码的值需要加一个偏移量 127 。==例如:1.011 × 2 ^ 3的原始阶码是3,按规定加上127后等于130,存储到8位空间,即为 1000 0010。

将10进制数转换为float类型的浮点数

将 128.36 转换为浮点数的流程如下:

(1)是正数,即符号位为0

(2)把10进制转成2进制:
整数部分:128(D)=1000 0000(B)
小数部分:0.36(D) = 0.01011100001010011……(B)(可根据需要截断)

(3)写成规范化形式:1000 0000. 0101 1100 0010 1001 = 1.000 0000 0101 1100 0010 1001 × 2 ^ 7
(4)指数为7,阶码要加上偏移量127,即E = 7 + 127 = 134(D)= 1000 0110(B)
(5)因为最前面的1是可以被隐含表示的,所以尾数M = 0.000 0000 0101 1100 0010 1001= 000 0000 0101 1100 0010 1001
最终结果为:0 10000110 00000000101110000101001
验证一下:
验证成立~

double类型

double占用 64 位的存储空间,64 位被分为了如下的三个部分:

这三部分的定义是和float类型一致的,只是位宽不同。需要注意的是,由于位宽的变化,所以double的阶码的偏移值不再是127,而是 1023。

除了这两种较为常用的类型外,其实IEEE754还规定了几种其他类型,但是都不太常用,所以不赘述了。

非规约化

当阶码E不全为0,也不全为1时,该浮点数称为**规约化(normal)形式。上面介绍的都是规约化形式的浮点数。当阶码E全为0时,该浮点数称为非规约化(subnormal)**形式。根据尾数的不同,可再分为2种形式:

尾数M为全 0 时,表示 0 ,视符号位而定是+0还是-0(二者在某些场景有区别)
尾数M不全为 0 时,表示非规约化小数
非规约化小数的定义和规约化小数之间存在如下区别:

规约化小数的尾数约定了含有一个隐藏的前导1,也就说真正表示的值是1.xxx;而非规约化小数的尾数则约定含有一个隐藏的前导0,即真正的值为0.xxx。
规约化小数的阶码需要加一个偏移量127,而非规约化小数的阶码需要加一个偏移量 126
非规约化小数可以用来表示那些非常小的接近0的数。

小结


浮点数是在计算机中表示数字的一种方式,能够表示更广泛的数值范围和更高的精度。浮点数通过将数字分为尾数和指数部分,以科学记数法的形式存储,从而灵活调整小数点的位置。这使得浮点数适合表示极大或极小的数值,广泛应用于科学计算、工程模拟和图形处理等领域。

==尽管浮点数具有较高的精度和表现范围,但其运算效率通常低于定点数,且在硬件实现上更为复杂。浮点运算可能引入舍入误差,这可能影响结果的准确性,因此在某些场合需特别注意。==在现代计算中,浮点数主要用于需要高精度的小数表示,而定点数则常用于对性能要求高且资源有限的场景。

浮点数适用任务:

高保真音频处理:在FPGA设计的数字音频工作站中,使用浮点数的FIR滤波器来处理24-bit/96 kHz的音频流。具体应用为实时均衡器功能,其中每个频段的增益调整会影响到信号的幅度,浮点数运算(如32-bit浮点)能精确计算频率响应,避免由于量化误差导致的音质下降。比如,调整0.5 dB的增益时,浮点数保证细微变化的正确表示。

图像处理:在FPGA实现的图像处理系统中,处理1080p实时视频流,使用浮点数的FIR滤波器进行图像去噪和锐化。举例来说,当对每帧视频应用高通滤波器以增强边缘时,浮点数(如32-bit浮点)可以处理非常小的像素变化(如从0.123到0.124),确保输出图像的细节清晰,不出现模糊现象。

定点数适用任务:

嵌入式信号处理:使用定点数(如Q15格式)进行音频效果处理,例如混响和回声消除。具体操作中,输入音频信号(16-bit定点数)通过定点FIR滤波器进行处理,确保实时音频处理延迟低于10毫秒,使得用户体验流畅自然。

无线通信:在FPGA设计的LTE基站中,使用定点数的FIR滤波器对接收到的信号进行解调和去噪。假设系统需要处理16-QAM调制信号,定点数格式(如Q11)允许在硬件资源有限的情况下,快速进行多通道信号处理,确保信号误码率低于1%并维持系统的高吞吐量。

三、定浮点数转换

浮点数到定点数

将浮点数转为定点数,也叫做 浮点数的定点化。

定点化首先需要约定好定点数的规格:**用几位表示整数,用几位表示小数,要不要表示符号位?**这些规格需要根据输入数据的范围和特性而定(这些往往在算法阶段确定)。

假如要输入的数据 a 的范围在 -9~5 之间,要求精度保留5位。那么绝对值最大的| -9 |至少需要用4位(1001)才能表示,所以整数部分为4位。综上,数据 a 的定点化规格应为:符号位1位,整数部分4位,小数部分5位,共10位。

假设a的一个值是 3.1415,那么它的定点化过程如下:

小数部分5位,则分辨率为2 ^ -5=0.03125。3.1415转化为2进制小数相当于在算需要多少个分辨率小数来表示它,所以转化过程为 3.1415 / 2 ^ -5 = 3.1415 × 2 ^ 5 = 100.528。
这个结果只能取整数部分,因为二进制小数相对其分辨率来说,只能表示整数个。例如两位小数的分辨率是1/4 = 0.25,那么就表示0.01(B)即0.25(D)的0-3倍,即0、0.25、0.5、0.75。
取整的方法有两种:
直接截去小数部分(truncate),这相当于数学上的向下取整(floor),就电路设计角度而言,截去的实现是很简单的,所以这种方法最为常用;
四舍五入(round),这样产生的误差比直接截断的误差小,但是需要多余的电路来实现,因此不太常用。
将结果100.528的小数部分截掉,为 100(D),可以理解为需要100个0.03125才能表示,即3.1415(D) = 100(D) × 0.00001(B),所以只要将100转化为2进制表示即可,即100(D)= 1100100(B)。因为设计的位宽是10位,所以需要补上符号位和在整数部分的高位补0,即最终结果为 0_0011_00100 。

0_0011_00100表示的值为 +3.125,它和原始数之间的差值就是 量化误差,为|3.125 - 3.1415| = 0.0165。量化误差是在量化过程中因为截断或四舍五入所直接产生的,但本质上还是因为有限个2进制编码无法表示无穷个10进制小数。

总结:浮点数乘以2^Q,然后四舍五入或截去小数,就是定点数,其中Q为小数位数。
再比如:
假设我们有一个浮点数 3.75,我们想将其转换为一个 16 位的定点数格式,其中小数位数为 8 位。
选择定点格式:总位数:16位;小数位数:8位;整数位数:16 - 8 = 8位。
使用公式:定点数 = 浮点数 × 2^小数位数
计算:3.75 × 2^8 = 3.75 × 256 = 960
四舍五入:
在这个例子中,960已经是一个整数,不需要进一步四舍五入。
存储:
将 960 转换为二进制表示:960 = 0011100000000000(在16位中)
最终,3.75 被定点化为 0011100 000000000,可以用作后续计算。

无损定点化

这里再引入一个无损定点化的概念,所谓【无损定点化】只是数学概念,==只要量化误差小于精度的一半,就认为是“无损”的。==按照这个标准,那对小数点采取四舍五入的结果必然是无损的。但是校招时很多题采取的是“量化后直接去除小数”,即我前面提到的直接去掉小数部分,那么最终的结果小数部分大于 0.5 则不是无损的了。

什么意思?
我还是拿 3.1415举例,定点化过程中首先利用公式:定点数 = 浮点数 × 2^小数位数(按5位), 3.1415 × 2 ^ 5 = 100.528,这里的小数部分大于了0.5,那么我们如果直接舍弃,就可以认为这个转化是有损的,当然我们也可以计算出他的量化误差:0.528/28=0.0020625,很明显这个值是大于量化精度的一半29=0.0019531,所以是有损的,用这两种办法我们都可以用来判断转化有损还是无损,位宽是否合适等。

定点数到浮点数

还是要先说明,这个浮点数不是IEEE754规定的浮点数,仅指10进制小数,显然这就是定点化的逆过程。以 定点数0_0011_00100 的转换为例:

首先需要确定该定点数的规格,假设其规格如下:1位符号位 + 4位整数部分 + 5位小数部分
符号位为0说明这是一个正数
整数部分的值为0011,即10进制的3
小数部分的值为00100,即10进制的0.125(可以理解为0.03125×4)
综合起来的结果就是 +3.125
整个过程相对简单,只要将整数部分和小数部分分别从2进制转换为10进制,再结合起来即可。

四、定点数据的两种溢出处理模式

两种常见的溢出处理方式:Saturate(饱和)和Wrap(绕回/截断)。

饱和处理 (Saturate)
饱和逻辑是当数据超过能够表示的范围时,直接将结果固定在最大值或最小值。
例如,假设你有8位有符号数(范围为-128到127),如果运算结果为150,那么饱和处理会将它限制为127(正向溢出),如果结果为-200,则饱和为-128(负向溢出)。

绕回处理 (Wrap / 截断)
绕回处理是直接忽略溢出的高位,只保留符合当前位宽的部分。例如,对于一个8位无符号数(范围为0到255),如果运算结果为300,二进制表示为100101100,那么系统只保留低8位,结果为00101100,即十进制44。

尽管饱和和绕回的处理不能保留原本正确的数值,但它们的目的并不是为了提供精确的数学结果,而是为了保证在硬件资源有限的情况下(如固定位宽)能有效处理溢出,避免程序崩溃或不稳定。

怎么尽可能的保留原本正确的溢出数值?
1、增加位宽(扩展数据位宽)
解决方案:如果系统允许,增加数据处理的位宽。例如,从8位扩展到16位或32位。
2、多字节存储
通过使用多个字节来存储超出位宽的数据,这样当数据溢出时,能够利用额外的字节来存储溢出的部分,从而保持正确的结果。
假设我们在一个8位无符号系统中表示的最大数是255。如果两个数相加会超过255,就会发生溢出。比如:
200+100=300,但8位系统只能表示到255,所以结果会变为44(300 - 256)。
那我们就可以将数据分为高位字节和低位字节来表示:
300可以表示为两个字节:高位字节1(表示256的一部分)和低位字节44(D)(剩余部分)。
高位字节存储溢出的部分,低位字节存储余数部分,这样可以正确存储300。

五、定点数的舍入模式

在数据处理过程中,为了防止数据溢出而增加位宽是一种很常见的处理方式,但是随着算法链的深入,如果任由位宽无限度地增长的话,就是导致使用资源爆炸。所以为了防止这种情况的出现,我们需要对数据的位宽做一定的截位处理,即舍入。

这些舍入模式可以通过减少数值精度、截断小数部分等方式,限制数据的位宽,从而控制资源使用。它们在硬件设计中,特别是嵌入式系统、FPGA和ASIC等资源有限的场景中,有助于防止位宽无限增长导致的资源爆炸。

  1. Round(四舍五入)
    规则:舍入到最接近的整数,对于0.5这种特殊情况,正数向上舍入,负数向下舍入。
    作用:这是我们日常最常见的舍入规则,通常用于处理普通的四舍五入场景。例如,处理货币值时,经常需要将小数部分四舍五入到最接近的整数。

硬件开销:是一个正数时,相当于舍弃小数部分然后加上小数部分的最高位;是一个负数,且小数部分的最高位为1和其他位不为全0时,相当于舍弃小数部分然后加上1,否则是于舍弃小数部分然后加上0。所以硬件开销是一个判断是否最高位为1和其他位不为全0的电路,一个MUX来选择进位值,一个加法电路实现整数部分加上进位值。

2. Nearest(最近整数舍入)
规则:与round类似,但对于0.5,无论正负数,都是向上舍入。
作用:这种舍入方式保证了对0.5及其绝对值较大的数,统一向上舍入,避免了round在正负数之间的非对称性。它适合不需要严格保持舍入对称的应用场景。

硬件开销:当小数部分的最高位为0时,相当于整数部分 + 进位值,进位值等于0,即小数部分的最高位;当小数部分的最高位为1时,相当于整数部分 + 进位值,进位值等于1,即小数部分的最高位。综合起来就是,直接用整数部分+小数部分的最高位。所以硬件开销只需要一个加法器。

3. Convergent(偶数舍入,银行家舍入法)
规则:舍入到最近的偶数,避免对0.5进行一致向上或向下舍入。
作用:这种舍入方式可以平衡大量舍入操作中的累积误差,特别适合大量数据计算中防止系统性误差(例如,一致的向上舍入会导致结果偏大)。convergent 舍入在金融计算和高精度浮点数运算中被广泛使用。

硬件开销:当小数部分的最高位为1且其他位为全0时,相当于整数部分 + 进位值,进位值等于整数部分的最低位;情况情况相当于整数部分 + 进位值,进位值等于小数部分的最高位。所以硬件开销是一个判断是否最高位为1和其他位为全0的电路,一个MUX来选择进位值,一个加法电路实现整数部分加上进位值。

4. Floor(向下取整)
规则:无论正负数,始终向下舍入到最接近的整数。
作用:在计算中,它确保所有的值都向着负无穷方向舍入,特别适合需要保持“保守估计”的场景。
硬件开销:只要丢掉小数部分(或者约定的精度外部分),相当于重连线,硬件开销很小(可以说是0开销!)

5. Ceil(向上取整)
规则:无论正负数,始终向上舍入到最接近的整数。
作用:适用于需要确保结果不会小于某个最小值的场景,可以在许多计算中确保“乐观估计”。
硬件开销:向上取整,因为丢掉小数部分相当于向下取整,所以向上取整只需要丢掉小数部分,然后加1。特殊情况是整数的情况,此时的向上取整值就是其自身,所以丢掉小数部分后加的不是1而是0。所以硬件开销需要1个加法器来做加法,还需要一个电路来判断是否为整数(即小数部分是否为全0),该电路的输出可以直接用作加法器的一个输入

6. Fix(向0取整)
规则:正数向下取整,负数向上取整,舍入方向总是向着0。
作用:这种方式适合需要绝对数值减小的场景,确保取整后数值绝对值比原来的小。

硬件开销:向0取整,相当于正数floor,负数ceil。因为正数丢掉小数部分相当于向下取整,负数丢掉小数部分是向上取整,所以正数是丢掉小数部分然后加0;负数则是丢掉小数部分然后+1。特殊情况是整数的情况,此时的取整值就是其自身,所以丢掉小数部分后加的不是1而是0。综合起来就是负的非整数是丢掉小数部分然后加1;其他情况则是丢掉小数部分然后加0。所以硬件开销需要1个加法器来做加法,还需要一个电路来判断是否为负的非整数(即小数部分是否为全0以及符号位为1),该电路的输出可以直接用作加法器的一个输入。

这些舍入模式定点浮点都可以用,硬件开销只举了定点的舍入例子,但他们的共同作用是通过不同的舍入策略,将数值转换为其最接近的整数或特定方向上的整数。这些舍入操作的核心目的是简化数据表示、减少计算复杂性或满足特定精度要求,同时尽量降低由于舍入产生的误差。
例如:
提高计算精度和效率:在数字系统中(如定点数和浮点数的计算),舍入操作能够在保证一定精度的同时简化数值表示,使得计算更加高效。这在硬件和软件实现中尤其重要,舍入可以帮助节省存储空间、提高运算速度,并在有限精度下避免无效的计算。
避免溢出或超出范围:舍入模式有助于在处理数值边界时,控制数值的范围。例如,floor和ceil可以分别确保数值不会超出预期的下限或上限,而fix则可以将数值收敛到更小的范围,避免正负溢出。
减少累积误差:对于连续计算,特别是涉及大量加法或乘法时,累积误差是一个常见问题。convergent模式的作用就是通过趋向偶数来平衡舍入误差,避免由于单方向舍入而引发的系统性偏差。
确保计算结果一致性:不同的舍入模式在某些场景下有助于保持结果的可预测性和一致性。例如,在处理金融数据或货币计算时,nearest模式可以确保在舍入时不论正负都采取同样的策略,以保证交易和计算结果的一致性。
满足特定的算法或约束要求:不同的舍入模式可以根据不同应用的约束要求来选择。例如,在信号处理、控制系统、图像处理等应用中,round可以使计算保持在所需的整数范围内,而ceil和floor则可以确保结果符合上下界的限制。

参考来源:https://wuzhikai.blog.csdn.net/?type=blog

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

星空lg

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值