用过DSP的应该都知道Q格式吧;

1 前言

Q格式是二进制的定点数格式,相对于浮点数,Q格式指定了相应的小数位数和整数位数,在没有浮点运算的平台上,可以更快地对浮点数据进行处理,以及应用在需要恒定分辨率的程序中(浮点数的精度是会变化的);需要注意的是Q格式是概念上小数定点,通过选择常规的二进制数整数位数和小数位数,从而达到所需要的数值范围和精度,这里可能有点抽象,下面继续看介绍。

2 Q数据的表示

2.1 范围和精度

定点数通常表示为\(Q_{m.n}\),其中m为整数个数,n为小数个数,其中最高位位符号位并且以二进制补码的形式存储;

  • 范围:\([-(2^{m-1}),2^{m-1}-2^{-n}]\)
  • 精度:\(2^{-n}\)

无符号的用\(UQ_{m.n}\)表示;

  • 范围:\([0,2^m-2^{-n}]\)
  • 精度:\(2^{-n}\)

2.2 推导

无符号Q格式数据的推导

这里以一个16位无符号整数为例,\(UQ_{9.7}\)所能表示的最大数据的二进制形式如下图所示;

所以不难看出,\(UQ_{9.7}\)的范围大小和精度;

根据等比数列求和公式得到,整数域最大值如下:

\[Sumi = 2^8+2^7+2^6+2^5+2^4+2^3+2^2+2^1+2^0 = 2^9 -1
\]

小数域最大值如下:

\[Sumf = 2^{-1}+2^{-2}+2^{-3}+2^{-4}+2^{-5}+2^{-6}+2^{-7}=1-2^{-7}
\]

因此\(UQ_{9.7}\)的范围满足 \([0,2^9-2^{-7}]\);

有符号Q格式数据的推导

这里以一个16位有符号整数为例,\(UQ_{9.7}\)所能表示的最大数据的二进制形式如下图所示;

所以不难求出,\(UQ_{9.7}\)的范围大小和精度;

根据等比数列求和公式得到,整数域最大值如下:

\[Sumi = 2^7+2^6+2^5+2^4+2^3+2^2+2^1+2^0 = 2^8 -1
\]

小数域最大值如下:

\[Sumf = 2^{-1}+2^{-2}+2^{-3}+2^{-4}+2^{-5}+2^{-6}+2^{-7}=1-2^{-7}
\]

因此\(Q_{9.7}\)最大能表示的数为: \(2^8-2^{-7}\);

\(Q_{9.7}\)所能表示的最小数据的二进制形式如下图所示;

可以从图中看到,该数表示为\(-2^8\);

补充一下:负数在计算机中是补码的形式存在的,补码=反码+1,符号位为1则表示为负数;

那么-4该如何表示呢?

8 bit数据为例,如下所示;

原码:0B 0000 100

反码:0B 1111 011

补码:0B 1111 100

综上,可以得到有符号\(Q_{9.7}\)的范围是:\([-2^8,2^8-2^{-7}]\)

3 Q数据的运算

3.1 0x7FFF

最大数的十六进制为0x7FFF,如下图所示;

3.2 0x8000

最小数的十六进制为0X8000,如下图所示;

上述这两种情况,下面都会用到。

3.3 加法

加法和减法需要两个Q格式的数据定标相同,即\(Q_{m_1.n_1}\)和\(Q_{m_2.n_2}\)满足以下条件;

\[\begin{cases} m_1 = m_2 \\
n_1 = n_2
\end{cases}\]

int16_t q_add(int16_t a, int16_t b)
{
return a + b;
}

上面的程序其实并不安全,在一般的DSP芯片具有防止溢出的指令,但是通常需要做一下溢出检测,具体如下所示;

int16_t q_add_sat(int16_t a, int16_t b)
{
int16_t result;
int32_t tmp; tmp = (int32_t)a + (int32_t)b;
if (tmp > 0x7FFF)
tmp = 0x7FFF;
if (tmp < -1 * 0x8000)
tmp = -1 * 0x8000;
result = (int16_t)tmp; return result;
}

3.4 减法

类似于加法的操作,需要相同定标的两个Q格式数进行相减,但是不会存在溢出的情况;

//https://great.blog.csdn.net/
int16_t q_sub(int16_t a, int16_t b)
{
return a - b;
}

3.5 乘法

乘法同样需要考虑溢出的问题,这里通过sat16函数,对溢出做了处理;

//https://great.blog.csdn.net/
// precomputed value:
#define K (1 << (Q - 1)) // saturate to range of int16_t
int16_t sat16(int32_t x)
{
if (x > 0x7FFF) return 0x7FFF;
else if (x < -0x8000) return -0x8000;
else return (int16_t)x;
} int16_t q_mul(int16_t a, int16_t b)
{
int16_t result;
int32_t temp; temp = (int32_t)a * (int32_t)b; // result type is operand's type
// Rounding; mid values are rounded up
temp += K;
// Correct by dividing by base and saturate result
result = sat16(temp >> Q); return result;
}

3.6 除法

//https://great.blog.csdn.net/
int16_t q_div(int16_t a, int16_t b)
{
/* pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format) */
int32_t temp = (int32_t)a << Q;
/* Rounding: mid values are rounded up (down for negative values). */
/* OR compare most significant bits i.e. if (((temp >> 31) & 1) == ((b >> 15) & 1)) */
if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0)) {
temp += b / 2; /* OR shift 1 bit i.e. temp += (b >> 1); */
} else {
temp -= b / 2; /* OR shift 1 bit i.e. temp -= (b >> 1); */
}
return (int16_t)(temp / b);
}

4 常见Q格式的数据范围

定点数\(X_q\)和浮点数\(X_f\)转换的关系满足以下公式:

\[\begin{cases}
X_q = (int)X_f*2^n \\
\\
X_f = (float)X_f*2^{-n}
\end{cases}\]

其中\(X_q\)为\(Q_{m.n}\),m表示整数位数,n表示小数位数;

#include <stdio.h>
#include <stdint.h>
#include <math.h> int main()
{
// 0111 1111 1111 1111
int16_t q_max = 32767; // 0x7FFF
// 1000 0000 0000 0000
int16_t q_min = -32768; // 0x8000
float f_max = 0;
float f_min = 0;
printf("\r\n");
for (int8_t i = 15; i>=0; i--) {
f_max = (float)q_max / pow(2,i);
f_min = (float)q_min / pow(2,i); printf("\t| Q %d | Q %d.%d| %f | %f |\r\n",
i,(15-i),i,f_max,f_min);
} return 0;
}

运行得到结果如下所示;

Q 格式 Qmn Max Min
Q 15 Q 0.15 0.999969 -1.000000
Q 14 Q 1.14 1.999939 -2.000000
Q 13 Q 2.13 3.999878 -4.000000
Q 12 Q 3.12 7.999756 -8.000000
Q 11 Q 4.11 15.999512 -16.000000
Q 10 Q 5.10 31.999023 -32.000000
Q 9 Q 6.9 63.998047 -64.000000
Q 8 Q 7.8 127.996094 -128.000000
Q 7 Q 8.7 255.992188 -256.000000
Q 6 Q 9.6 511.984375 -512.000000
Q 5 Q 10.5 1023.968750 -1024.000000
Q 4 Q 11.4 2047.937500 -2048.000000
Q 3 Q 12.3 4095.875000 -4096.000000
Q 2 Q 13.2 8191.750000 -8192.000000
Q 1 Q 14.1 16383.500000 -16384.000000
Q 0 Q 15.0 32767.000000 -32768.000000

5 0x5f3759df

Q格式虽然十分抽象,但是且看看这个数字0x5f3759df,感觉和Q格式有某种联系,它是雷神之锤3中的一个算法的魔数,毕竟游戏引擎需要充分考虑到效率,具体的由来可以看一下论文《Fast Inverse Square Root》,下面是源码中剥出来的快速平方根算法;

float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F; x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed #ifndef Q3_VM
#ifdef __linux__
assert( !isnan(y) ); // bk010122 - FPE?
#endif
#endif
return y;
}

6 总结

本文介绍了Q格式的表示方式以及相应的运算,另外需要注意在Q格式运算的时候,两者定标必须相同,对于数据的溢出检测也要做相应的处理。


作者能力有限,文中难免有错误和纰漏之处,请大佬们不吝赐教

创作不易,如果本文帮到了您;

请帮忙点个赞

基于C语言的Q格式使用详解的更多相关文章

  1. 基于双向BiLstm神经网络的中文分词详解及源码

    基于双向BiLstm神经网络的中文分词详解及源码 基于双向BiLstm神经网络的中文分词详解及源码 1 标注序列 2 训练网络 3 Viterbi算法求解最优路径 4 keras代码讲解 最后 源代码 ...

  2. [转帖]IP /TCP协议及握手过程和数据包格式中级详解

    IP /TCP协议及握手过程和数据包格式中级详解 https://www.toutiao.com/a6665292902458982926/ 写的挺好的 其实 一直没闹明白 网络好 广播地址 还有 网 ...

  3. Go语言Slice作为函数参数详解

    Go语言Slice作为函数参数详解 前言 首先要明确Go语言中实质只有值传递,引用传递和指针传递是相对于参数类型来说. 个人认为上诉的结论不对,把引用类型看做对指针的封装,一般封装为结构体,结构体是值 ...

  4. 基于集合成工控机Ubuntu系统安装分区详解

    基于集合成工控机Ubuntu系统安装分区详解 硬件描述:双核的CPU,128G的固态硬盘 软件描述:使用Ubuntu12.04系统,内核3.8.0-29版本,QT4.8.1版本 1.新建分区表 /de ...

  5. spring基于通用Dao的多数据源配置详解【ds1】

    spring基于通用Dao的多数据源配置详解 有时候在一个项目中会连接多个数据库,需要在spring中配置多个数据源,最近就遇到了这个问题,由于我的项目之前是基于通用Dao的,配置的时候问题不断,这种 ...

  6. 基于ORA-12170 TNS 连接超时解决办法详解

    转自原文 基于ORA-12170 TNS 连接超时解决办法详解 1.开始----程序-----Oracle------配置和移植工具-----Net Manager----本地----服务命名---o ...

  7. C 语言 printf格式控制详解

    闲来无事,整理了一下C语言printf() 的格式控制语句. PS:详细来源于网络. printf的格式控制的完整格式: %  -  0  m.n  l或h  格式字符 下面对组成格式说明的各项加以说 ...

  8. 基于python的直播间接口测试实战 详解结合项目

    基于python的直播间接口测试详解 一.基本用例内容描述 以设置白名单 /advisor/setUserWhiteList.do接口为例,该方法为POST at first,先要导入一些常用到的模块 ...

  9. IdentityServer4实战 - 基于角色的权限控制及Claim详解

    一.前言 大家好,许久没有更新博客了,最近从重庆来到了成都,换了个工作环境,前面都比较忙没有什么时间,这次趁着清明假期有时间,又可以分享一些知识给大家.在QQ群里有许多人都问过IdentityServ ...

随机推荐

  1. Hadoop权威指南(中文版-带目录索引)pdf电子书

      Hadoop权威指南(中文版-带目录索引)pdf电子书下载地址:百度网盘点击下载:链接:https://pan.baidu.com/s/1E-8eLaaqTCkKESNPDqq0jw 提取码:g6 ...

  2. mysql 创建表 索引 主键 引擎 自增 注释 编码等

    CREATE TABLE text(id INT(20) COMMENT '主键',NAME VARCHAR(20) COMMENT '姓名',PASSWORD VARCHAR(20) COMMENT ...

  3. 2. js的异步

    1. 回掉2. promise3. Generator4. Async/await

  4. [转]ThinkCMF框架任意内容包含漏洞分析复现

    0x00 简介 ThinkCMF是一款基于PHP+MYSQL开发的中文内容管理框架,底层采用ThinkPHP3.2.3构建.ThinkCMF提出灵活的应用机制,框架自身提供基础的管理功能,而开发者可以 ...

  5. testNG 预期异常、忽略测试、超时测试

    通过@Test 注解的参数值实现如下的几种测试 一.通过 @Test(expectedExceptions=异常类名) 参数实现到达 预期指定的异常效果 @Test(expectedException ...

  6. 随笔之——浮动(float)的影响及其清除、、clear与overflow-hidden清除float的不同!!!

    一.浮动(float)对内联元素的影响. 1.我们都知道,内联元素(例如:span/img/input...)在正常情况下不可以对其设置宽高的,它的大小之只和它内部内容的多少有关. 我们怎样才可以对其 ...

  7. PHP Ajax 跨域问题解决方案

    本文通过设置Access-Control-Allow-Origin来实现跨域. 例如:客户端的域名是client.0751.tv,而请求的域名是server.0751.tv. 如果直接使用ajax访问 ...

  8. LeetCode7-ReverseInteger

    LeetCode7-ReverseInteger LeetCodeeasyOverflow 题目 题目所在链接为 LeetCode-7:ReverseInteger 题目描述 给出一个32位的有符号整 ...

  9. java 中的fork join框架

    文章目录 ForkJoinPool ForkJoinWorkerThread ForkJoinTask 在ForkJoinPool中提交Task java 中的fork join框架 fork joi ...

  10. Spring Boot Starters介绍

    文章目录 Web Start Test Starter Data JPA Starter Mail Starter 结论 对于任何一个复杂项目来说,依赖关系都是一个非常需要注意和消息的方面,虽然重要, ...