浮點數據如何采用定點方式處理


  原文連接:http://www.eepw.com.cn/article/17893.htm

  許多芯片只支持整數運算,如果現在這些芯片上進行小數運算的話,定點小數運算應該是最佳選擇了,此外即使芯片支持浮點數,定點小數運算也是最佳的速度選擇。

        在世界中,由於芯片的限制,經常使用定點小數運算。所謂定點小數,實際上就是用整數來進行小數運算。下面先介紹定點小數的一些理論知識,然后以C語言為例,介紹一下定點小數運算的方法。在TI C5000 DSP系列中使用16比特為最小的儲存單位,所以我們就用16比特的整數來進行定點小數運算。

        先從整數開始,16比特的儲存單位最多可以表示0x0000到0xffff,65536種狀態,如果它表示C語言中的無符號整數的話,就是從0到65535。如果需要表示負數的話,那么最高位就是符號位,而剩下的15位可以表示32768種狀態。這里可以看出,對於計算機或者DSP芯片來說,符號並沒有什么特殊的儲存方式,其實是和數字一起儲存的。為了使得無論是無符號數還是符號數,都可以使用同樣的加法減法規則,符號數中的負數用正數的補碼表示。

        我們都知道-1 + 1 =0,而0x0001表示1,那么-1用什么來表示才能使得-1 + 1 =0呢?答案很簡單:0xffff。現在就可以打開Windows的計算器,用16進制計算一下0xffff+0x0001,結果是0x10000。那么0x10000和0x0000等價麽,我們剛才說過用16比特來表達整數,最高位的1是第17位,這一位是溢出位,在運算寄存器中沒有儲存這一位,所以結果是低16位,也就是0x0000。現在我們知道負數的表達方式了。舉個例子:-100。首先我們需要知道100的16進制,用計算器轉換一下,可以知道是0x0064,那么-100就是0x10000 - 0x0064,用計算器算一下得0xff9c。還有一種簡單的轉換符號的方法,就是取反加一:把數x寫成二進制格式,每位0變1,1變0,最后把結果加1就是-x了。

        好,復習了整數的相關知識之后,我們進入定點小數運算環節。所謂定點小數,就是小數點的位置是固定的。我們是要用整數來表示定點小數,由於小數點的位置是固定的,所以就沒有必要儲存它(如果儲存了小數點的位置,那就是浮點數了)。既然沒有儲存小數點的位置,那么計算機當然就不知道小數點的位置,所以這個小數點的位置是我們寫程序的人自己需要牢記的。

         先以10進制為例。如果我們能夠計算12+34=46的話,當然也就能夠計算1.2+3.4 或者 0.12+0.34了。所以定點小數的加減法和整數的相同,並且和小數點的位置無關。乘法就不同了。 12*34=408,而1.2*3.4=4.08。這里1.2的小數點在第1位之前,而4.08的小數點在第2位之前,小數點發生了移動。所以在做乘法的時候,需要對小數點的位置進行調整?!可是既然我們是做定點小數運算,那就說小數點的位置不能動!!怎么解決這個矛盾呢,那就是舍棄最低位。 也就說1.2*3.4=4.1,這樣我們就得到正確的定點運算的結果了。所以在做定點小數運算的時候不僅需要牢記小數點的位置,還需要記住表達定點小數的有效位數。上面這個例子中,有效位數為2,小數點之后有一位。

       現在進入二進制。我們的定點小數用16位二進制表達,最高位是符號位,那么有效位就是15位。小數點之后可以有0 - 15位。我們把小數點之后有n位叫做Qn,例如小數點之后有12位叫做Q12格式的定點小數,而Q0就是我們所說的整數。

       Q12的正數的最大值是 0 111 . 111111111111,第一個0是符號位,后面的數都是1,那么這個數是十進制的多少呢,很好運算,就是 0x7fff / 2^12 = 7.999755859375。對於Qn格式的定點小數的表達的數值就它的整數值除以2^n。在計算機中還是以整數來運算,我們把它想象成實際所表達的值的時候,進行這個運算。

       反過來把一個實際所要表達的值x轉換Qn型的定點小數的時候,就是x*2^n了。例如 0.2的Q12型定點小數為:0.2*2^12 = 819.2,由於這個數要用整數儲存, 所以是819 即 0x0333。因為舍棄了小數部分,所以0x0333不是精確的0.2,實際上它是819/2^12 =0.199951171875。

我們用數學表達式做一下總結:
x表示實際的數(*一個浮點數), q表示它的Qn型定點小數(一個整數)。
q = (int) (x * 2^n)
x = (float)q/2^n

由以上公式我們可以很快得出定點小數的+-*/算法:
假設q1,q2,q3表達的值分別為x1,x2,x3
q3 = q1 + q2   若 x3 = x1 + x2
q3 = q1 - q2   若 x3 = x1 - x2
q3 = q1 * q2 / 2^n若 x3 = x1 * x2
q3 = q1 * 2^n / q2若 x3 = x1 / x2
我們看到加減法和一般的整數運算相同,而乘除法的時候,為了使得結果的小數點位不移動,對數值進行了移動。
用c語言來寫定點小數的乘法就是:
short q1,q2,q3;
....
q3=((long q1) * (long q2)) >> n;

由於/ 2^n和* 2^n可以簡單的用移位來計算,所以定點小數的運算比浮點小數要快得多。下面我們用一個例子來驗證一下上面的公式:
用Q12來計算2.1 * 2.2,先把2.1 2.2轉換為Q12定點小數:
2.1 * 2^12 = 8601.6 = 8602
2.2 * 2^12 = 9011.2 = 9011
(8602 * 9011) >> 12 = 18923
18923的實際值是18923/2^12 = 4.619873046875 和實際的結果 4.62相差0.000126953125,對於一般的計算已經足夠精確了。

  還有一篇文章也值得參考: https://www.jianshu.com/p/ef2211ca0e88

 

Q表示    S表示    十進制數表示范圍

Q15    S0.15    -1≤x≤0.9999695

Q14    S1.14    -2≤x≤1.9999390

Q13    S2.13    -4≤x≤3.9998779

Q12    S3.12    -8≤x≤7.9997559

Q11    S4.11    -16≤x≤15.9995117

Q10    S5.10    -32≤x≤31.9990234

Q9     S6.9     -64≤x≤63.9980469

Q8     S7.8     -128≤x≤127.9960938

Q7     S8.7     -256≤x≤255.9921875

Q6     S9.6     -512≤x≤511.9804375

Q5     S10.5    -1024≤x≤1023.96875

Q4     S11.4    -2048≤x≤2047.9375

Q3     S12.3    -4096≤x≤4095.875

Q2     S13.2    -8192≤x≤8191.75

Q1     S14.1    -16384≤x≤16383.5

Q0     S15.0    -32768≤x≤32767

 

浮點至定點變換的C程序舉例

本節我們通過一個例子來說明C程序從浮點變換至定點的方法。這是一個對語音信號(0.3~3.4kHz)進行低通濾波的C語言程序,低通濾波的截止頻率為800Hz,濾波器采用19點的有限沖擊響應FIR濾波。語音信號的采樣頻率為8kHz,每個語音樣值按16位整型數存放在insp.dat文件中。

例1.7語音信號800Hz 19點FIR低通濾波C語言浮點程序。

#i nclude

const int length=180/*語音幀長為180點=22.5ms@8kHz采樣*/

void filter(int xin[],int xout[],int n,float h[]);/*濾波子程序說明*/

/*19點濾波器系數*/

static float h[19]=

{0.01218354,-0.009012882,-0.02881839,-0.04743239,-0.04584568,

-0.008692503,0.06446265,0.1544655,0.2289794,0.257883,

0.2289794,0.1544655,0.06446265,-0.008692503,-0.04584568,

-0.04743239,-0.02881839,-0.009012882,O.01218354};

static int xl[length+20];

/*低通濾波浮點子程序*/

void filter(int xin[],int xout[],int n,float h[])

{

int i,j;

float sum;

for(i=0;i

for(i=0;i<length;i++)

{

sum=0.0;

for(j=0;j<n;j++)sum+=h[j]*x1[i-j+n-1];

xout=(int)sum;

for(i=0;i<(n-l);i++)x1[n-i-2]=xin[length-1-i];

}

/*主程序*/

void main()

FILE *fp1,*fp2;

int ,indata[length],outdata[length];

fp1=fopen(insp.dat,"rb");/* 輸入語音文件*/

fp2=fopen(Outsp.dat,"wb");/* 濾波后語音文件*/

=0;

while(feof(fp1) ==0)

{

++;

printf(“=%d\n”,);

for(i=0;i<length;i++)indata=getw(fp1); /*取一幀語音數據*/

filter(indata,outdata,19,h);/*調用低通濾波子程序*/

for(i=0;i<length;i++)putw(outdata,fp2);/*將濾波后的樣值寫入文件*/

}

fcloseall();/*關閉文件*/

return(0);

}

例1.8語音信號800Hz l9點FIR低通濾波C語言定點程序。

#i nclude

const int length=180;

void filter (int xin[],int xout[],int n,int h[]);

static int h[19]={399,-296,-945,-1555,-1503,-285,2112,5061,7503,8450,

7503,5061,2112,-285,-1503,-1555,-945,-296,399};/*Q15*/

static int x1[length+20];

/*低通濾波定點子程序*/

void filter(int xin[],int xout[],int n,int h[])

int i,j;

long sum;

for(i=0;i<length;i++)x1[n+i-111=xin];

for(i=0;i<1ength;i++)

sum=0;

for(j=0;j<n;j++)sum+=(long)h[j]*x1[i-j+n-1];

xout=sum>>15;

for(i=0;i<(n-1);i++)x1[n-i-2]=xin[length-i-1];

}

主程序與浮點的完全一樣

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM