用过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. Daily Scrum 1/14/2016

    Zhaoyang & Yandong: Still optimizing the speech input interface Dong & Fuchen: Image asynchr ...

  2. Sprint 5 summary: UI 界面更新,Azure端部署和用户反馈分析 12/28/2015

    本次sprint主要完成的任务有对手机APP的UI界面的更新,同时对Azure客户端的部署进行了相应的学习和有关的程序设计.同时对于ALPHA release的用户反馈做出相应的分析以确定接下来工作的 ...

  3. Java 14 来势汹汹,这回让空指针无处遁形!!

    上篇:Java 14 之模式匹配,非常赞的一个新特性! 相信在坐的每一位 Java 程序员都遇到过空指针异常: NullPointerException(NPE),不甚其烦. 栈长之前也分享几篇避免空 ...

  4. Linux学习笔记(八)权限管理命令

    权限管理命令 权限位含义 chmod chown chgrp umask默认权限 权限位含义 第1位代表文件类型 "-":普通文件 "b":块设备文件 &quo ...

  5. react-i8n

    1. 在项目中安装 npm install react-intl --save 2.兼容Safari各个版本需要安装intl npm install intl --save 3.编写语言包 1.)新建 ...

  6. [git] github上传项目(使用git)、删除项目、添加协作者

    来源:http://www.cnblogs.com/sakurayeah/p/5800424.html (怕链接失败,所以直接就就复制过来啦,感谢作者) 一.注册github账号 github网址ht ...

  7. Java中的方法是什么以及方法的书写格式

    方法:完成特定功能的代码块注意:在很多语言里面有函数的定义,而在Java中函数被称为方法.方法格式:修饰符 返回值类型+ 方法名 (参数类型 参数名1,参数类型 参数名2...){方法体语句;retu ...

  8. js前端加密,php后端解密(crypto-js,openssl_decrypt)

    来源:https://blog.csdn.net/morninghapppy/article/details/79044026 案例:https://blog.csdn.net/zhihua_w/ar ...

  9. [Qt] 文本文件读写, 摘自官方文档

    Reading Files Directly The following example reads a text file line by line: QFile file("in.txt ...

  10. zookeeper笔记(二)

    title: zookeeper笔记(二) zookeeper ALC权限控制 getAcl path 可以查看某个node的权限 设置权限: 2. world方式 setAcl <path&g ...