用过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_tq_add(int16_ta,int16_tb) { returna+b; }上面的程序其实并不安全,在一般的DSP芯片具有防止溢出的指令,但是通常需要做一下溢出检测,具体如下所示;
复制//https://great.blog.csdn.net/ int16_tq_add_sat(int16_ta,int16_tb) { int16_tresult; int32_ttmp; tmp=(int32_t)a+(int32_t)b; if(tmp>0x7FFF) tmp=0x7FFF; if(tmp< -1*0x8000) tmp=-1*0x8000; result=(int16_t)tmp; returnresult; }3.4 减法
类似于加法的操作,需要相同定标的两个Q格式数进行相减,但是不会存在溢出的情况;
复制//https://great.blog.csdn.net/ int16_tq_sub(int16_ta,int16_tb) { returna-b; }3.5 乘法
乘法同样需要考虑溢出的问题,这里通过复制sat16函数,对溢出做了处理;
复制//https://great.blog.csdn.net/ //precomputedvalue: #defineK(1<< (Q – 1)) //saturatetorangeofint16_t int16_tsat16(int32_tx) { if(x>0x7FFF)return0x7FFF; elseif(x< -0x8000)return-0x8000; elsereturn(int16_t)x; } int16_tq_mul(int16_ta,int16_tb) { int16_tresult; int32_ttemp; temp=(int32_t)a*(int32_t)b;//resulttypeisoperandstype //Rounding;midvaluesareroundedup temp+=K; //Correctbydividingbybaseandsaturateresult result=sat16(temp>>Q); returnresult; }3.6 除法
复制//https://great.blog.csdn.net/ int16_tq_div(int16_ta,int16_tb) { /*pre-multiplybythebase(UpscaletoQ16sothattheresultwillbeinQ8format)*/ int32_ttemp=(int32_t)a<< Q; /*Rounding:midvaluesareroundedup(downfornegativevalues).*/ /*ORcomparemostsignificantbitsi.e.if(((temp>>31)&1)==((b>>15)&1))*/ if((temp>=0&&b>=0)||(temp< 0&&b< 0)){ temp+=b/2;/*ORshift1biti.e.temp+=(b>>1);*/ }else{ temp-=b/2;/*ORshift1biti.e.temp-=(b>>1);*/ } return(int16_t)(temp/b); }4 常见Q格式的数据范围
定点数和浮点数转换的关系满足以下公式:
其中为,复制m表示整数位数,复制n表示小数位数;
复制#include #include #include intmain() { //0111111111111111 int16_tq_max=32767;//0x7FFF //1000000000000000 int16_tq_min=-32768;//0x8000 floatf_max=0; floatf_min=0; printf(” “); for(int8_ti=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); } return0; }运行得到结果如下所示;
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.0000005 0x5f3759df
Q格式虽然十分抽象,但是且看看这个数字0x5f3759df,感觉和Q格式有某种联系,它是雷神之锤3中的一个算法的魔数,毕竟游戏引擎需要充分考虑到效率,具体的由来可以看一下论文复制《Fast Inverse Square Root》,下面是源码中剥出来的快速平方根算法;
复制floatQ_rsqrt(floatnumber) { longi; floatx2,y; constfloatthreehalfs=1.5F; x2=number*0.5F; y=number; i=*(long*)&y;//evilfloatingpointbitlevelhacking i=0x5f3759df-(i>>1);//whatthefuck? y=*(float*)&i; y=y*(threehalfs-(x2*y*y));//1stiteration //y=y*(threehalfs-(x2*y*y));//2nditeration,thiscanberemoved #ifndefQ3_VM #ifdef__linux__ assert(!isnan(y));//bk010122-FPE? #endif #endif returny; }6 总结
本文介绍了Q格式的表示方式以及相应的运算,另外需要注意在Q格式运算的时候,两者定标必须相同,对于数据的溢出检测也要做相应的处理。
审核编辑:汤梓红免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:Q格式的表示方式以及相应的运算-qy格式怎样转换成无损音乐 https://www.yhzz.com.cn/a/8665.html