用过dsp的应该都知道q格式吧;
1 前言
2 q数据的表示
2.1 范围和精度
2.2 推导
3 q数据的运算
3.1 0x7fff
3.2 0x8000
3.3 加法
3.4 减法
3.5 乘法
3.6 除法
4 常见q格式的数据范围
5 0x5f3759df
6 总结
1 前言 q格式是二进制的定点数格式,相对于浮点数,q格式指定了相应的小数位数和整数位数,在没有浮点运算的平台上,可以更快地对浮点数据进行处理,以及应用在需要恒定分辨率的程序中(浮点数的精度是会变化的);
需要注意的是q格式是概念上小数定点,通过选择常规的二进制数整数位数和小数位数,从而达到所需要的数值范围和精度,这里可能有点抽象,下面继续看介绍。
2 q数据的表示 2.1 范围和精度 定点数通常表示为,其中m为整数个数,n为小数个数,其中最高位位符号位并且以二进制补码的形式存储;
范围: 精度: 无符号的用表示;
范围: 精度: 2.2 推导 无符号q格式数据的推导这里以一个16位无符号整数为例,所能表示的最大数据的二进制形式如下图所示;
所以不难看出,的范围大小和精度;根据等比数列求和公式得到,整数域最大值如下:
小数域最大值如下:
因此的范围满足 ;
有符号q格式数据的推导这里以一个16位有符号整数为例,所能表示的最大数据的二进制形式如下图所示;
所以不难求出,的范围大小和精度;根据等比数列求和公式得到,整数域最大值如下:
小数域最大值如下:
因此最大能表示的数为:;
所能表示的最小数据的二进制形式如下图所示;
可以从图中看到,该数表示为;
补充一下:负数在计算机中是补码的形式存在的,补码=反码+1,符号位为1则表示为负数;
那么-4该如何表示呢?
以8 bit数据为例,如下所示;
原码:0b 0000 100
反码:0b 1111 011
补码:0b 1111 100
综上,可以得到有符号的范围是:
3 q数据的运算 3.1 0x7fff 最大数的十六进制为0x7fff,如下图所示;
3.2 0x8000 最小数的十六进制为0x8000,如下图所示;
上述这两种情况,下面都会用到。
3.3 加法 加法和减法需要两个q格式的数据定标相同,即和满足以下条件;
int16_t q_add(int16_t a, int16_t b){ return a + b;} 上面的程序其实并不安全,在一般的dsp芯片具有防止溢出的指令,但是通常需要做一下溢出检测,具体如下所示;
//https://great.blog.csdn.net/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 < 0x7fff) return 0x7fff; else if (x > 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 <> 31) & 1) == ((b >> 15) & 1)) */ if ((temp >= 0 && b >= 0) || (temp < 0 && b > 1); */ } else { temp -= b / 2; /* or shift 1 bit i.e. temp -= (b >> 1); */ } return (int16_t)(temp / b);} 4 常见q格式的数据范围 定点数和浮点数转换的关系满足以下公式:
其中为,m表示整数位数,n表示小数位数;
#include #include #include 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(); 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( | q %d | q %d.%d| %f | %f |, 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格式运算的时候,两者定标必须相同,对于数据的溢出检测也要做相应的处理。
智慧警务情报研判系统开发大数据分析平台搭建
新一代碳纤维地铁在广州试跑,地铁牵引系统节能15%以上
重磅!华为旗舰平板M6将和新机nova5系列一同推出
家用空调保养五个法则
轴承知识-磨削、超精加工技术和平面磨削基准面的选择原则
Q格式的表示方式以及相应的运算
音圈马达助力佳能发表VR拍摄系统
CAD中如何添加打印戳记(类似WORD的页眉、页脚)?
区块链应用爆发式增长对英伟达业务有何影响?
电子商务的崛起刺激了市场对电动配送货车的兴趣
dfrobot 肌电传感器简介
物联网的到来将会如何让万物都连接上网络
新一代黑鲨游戏手机曝光将采用8GB内存安卓9系统并搭载骁龙855处理器
气动焊接球阀的基本性能
解读应用材料财报 何以成为半导体设备巨头?
晶振的工作原理
通用移动电源的妙用
一款机身只有136mm厚的净水机——海尔物联网净水机“博观”
创想视觉焊缝跟踪系统适配广数机器人进行自动化焊接的应用
小米6什么时候上市:小米5C或同小米6在3月发布