單片機以性價比為特點,隨着能源行業的發展,單片機在數字能源中的運用越來越廣泛。最近學習了如何低端機上實現浮點運算。
1、前言
目前,大多數的單片機不具有浮點運算單元(FPU)。
TI公司的tms320f28335具有FPU,但是在進行一個浮點除法運算時,需要1.5us的時間,這在實時控制系統中是不太能接受的。
不具有浮點運算的單片機,需要將浮點運算轉換為可以接受的整形運算。
2、整形運算概述
單片機主要分為8位機、16位機和32位機,使用最多的就是16位機。
16位機中變量類型 int 是16位的,也就是說:16位機使用16位的0或者1組合表示數據。
32位機中變量類型 int 是32位的,也就是說:32位機使用32位的0或者1組合表示數據。
3、浮點數的近似轉換
在單片機中,浮點數乘除運算可以近似用乘以一個整數然后除以2的n次方表示。
例如:
0.25 = 1 >> 2;
0.5 = 1 >> 1;
0.75 = 3 >>2;
其他的浮點數以此類推。
一個浮點數可以有多種近似替換的方案,每種替換方案的精度不一樣。
比如:
0.8 可以近似等於3>>2(0.75),也可以近似等於13>>4(0.8125);
明顯可以看到,使用13>>4替換0.8比3>>2的精度要高些。但是前者更容易造成數據位溢出。
4、防止數據溢出
步驟三的轉換也是有前提條件的:變量乘了整數后防止數據超過最大值。
如果整形變量是16位的,那么它乘以一個數后,它也必須是16位的,不能超出原有變量的數據類型的范圍。
下面測試案例:
//定義變量類型 unsigned int a; unsigned int b; unsigned long c; unsigned int d; //測試方法 a = (1024*1024)>>10; b = ((long)(1024*1024))>>10; c = (1024*1024)>>10; d = __builtin_muluu(1024,1024)>>10; //編譯警告 Test.c:101:15: warning: integer overflow in expression Test.c:102:21: warning: integer overflow in expression Test.c:103:14: warning: integer overflow in expression //調試變量結果 a = 0 b = 0 c = 0 d = 1024
在16位單片機的C編譯器的作用下,由上面的測試結果顯示,僅僅只有第四種輸出正確結果。
其他三種,不管是強制數據轉換,還是設置為long型的變量,導致輸出結果都不正確。
可能:
在這款單片機中,內存的最大位數位16位吧,如果需要超出16位運算結果的數據,就會報錯。
或者只能按照該單片機自帶的函數處理數據吧。
5、與浮點Q變換的區別
浮點數a轉為定點數b是:b = (int)a*2n
定點數b轉為浮點數a是:a = (float)a>>n
Q變換固定了n的位數,因此,截斷誤差也是固定的。一般,Q變換是把浮點數全部轉為定點數參與運算,最后轉為才轉為浮點數;
上述提到的變換,截斷誤差取決於具體乘以的數和參數n;,這種變換,參與運算的等效為一個浮點數。
注意事項:
上述優化措施有一個很大的弊端,">>"運算很容易造成數據精度的丟失。
當一個整數A,乘以一個比1/A還小的數的時候,經過位移運算后,變為零。
比如:
對於系數0.75:
1*0.75 = 0.75 轉化為 1*3>>2 = 0; 丟失數值0.75,相對誤差100%;
2*0.75 = 1.5 轉化為 2*3>>2 = 1; 丟失數值0.5,相對誤差為33.3%;
10*0.75 = 7.5 轉化為 10*3>>2 = 7; 丟失數值0.5,相對誤差為6.6%;
20*0.75 = 15 轉化為 20*3>>2 = 15; 丟失數值0,相對誤差為0%;
100*0.75 = 75 轉化為100*3>>2=75; 丟失數值0,相對誤差為0%;
200*0.75 = 150 轉化為 200*3>>2 = 150; 丟失數值0,相對誤差為0%;
對於系數0.25:
1*0.25 = 0.25 轉化為 1>>2 = 0; 丟失數值0.25,相對誤差100%;
2*0.25 = 0.5 轉化為 2>>2 = 0; 丟失數值0.5,相對誤差為100%;
10*0.25 = 2.5 轉化為 10>>2 = 2; 丟失數值0.5,相對誤差為25%;
20*0.25 = 5 轉化為 20>>2 = 5; 丟失數值0,相對誤差為0%;
100*0.25 = 25 轉化為100>>2=25; 丟失數值0,相對誤差為0%;
200*0.25 = 50 轉化為 200>>2 = 50; 丟失數值0,相對誤差為0%;
對於系數0.125:
1*0.125 = 0.125 轉化為 1>>3 = 0; 丟失數值0.125,相對誤差100%;
2*0.125 = 0.25 轉化為 2>>3 = 0;丟失數值0.25,相對誤差為100%;
8*0.125 = 1 轉化為 8>>3 = 1; 丟失數值0,相對誤差為0%;
9*0.125=1.125 轉化為9>>3 = 1; 丟失數值0.125,相對誤差為11.11%
10*0.125 = 1.25 轉化為 10>>3 = 1; 丟失數值0.25,相對誤差為20%;
20*0.125 = 2.5 轉化為 20>>3= 2; 丟失數值0,相對誤差為20%;
100*0.125 = 12.5 轉化為100>>3=12; 丟失數值0.5,相對誤差為4%;
150*0.125 =18.75 轉化為150>>3=18; 丟失數值0.75,相對誤差為4%;
180*0.125=22.5 轉換為180>>3=22; 丟失數值0.5,相對誤差為2.22%;
200*0.125 = 25 轉化為 200>>3= 25; 丟失數值0,相對誤差為0%;
因此,
上述思路,對於乘同以小於1的系數(被乘數),需要整數A不能太小,
總結:分子防溢出,系數防丟失,我太難了。