1、浮點數如何存儲 大家在平時的嵌入式軟件開發過程中應該對整形的存儲形式會比較熟悉,因為我們進行底層寄存器的配置大部分都是使用無符號整形進行賦值寫入,然而對于有符號整形的存儲形式你是否已經了解清楚了?對于浮點類型的數據的存儲呢?好吧,今天這篇文章主要是對浮點類型數據進行講解,其他相關大家可以查閱相關資料閱讀學習,作者后續也會整理發布。 1)浮點存儲形式 對于語法等知識一般都是建立在一定的規范之上的,不然不利于技術的兼容統一發展,但是在不同的領域由于有著不同的需求,可能遵循的規范有所差異,對于浮點數的規范目前大部分系統都采用的是IEEE 754標準。我們這里以4字節單精度浮點類型為例子為大家講解一下浮點數的存儲形式 ![]() file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml3492\wps19.jpg 對于浮點數的存儲形式可以用2進制科學計數法表示: (符號:+/-)1.(二進制尾數)*2^(指數=實際指數+偏移量) 對于這幾個名詞不是特別好解釋,結合實際轉化過程會更加好理解 ![]() file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml3492\wps20.png · 對于小數部分的二進制表示采用除二反取的方法獲得; · · 23bit的尾數部分前面0001即為科學二進制的小數點后面部分,其他bit用0填充; · · 指數部分會添加一個偏移量127,這個僅僅是對于float類型;對于其他浮點類型由于表示的數據范圍不同偏移也不同,比如double需要+1023的偏移。 · · 由于我們舉例4.25 > 1,那么我們實際指數 > 0;如果我們的浮點是0.025,這個時候實際指數為負數,大家可以嘗試編碼轉化。 · 2)浮點精度問題 我們通過上面浮點數的存儲方式可以知道8bit的指數最大可以表示255,最大值的指數就是255 - 127 = 128,2^128 = 3.402..e+38;(確實非常大!) 如果用我們的4byte無符號整形表示最大數據為2^32,看起來遠遠小于浮點表示;不過大家是否想過一個問題,根據數據二進制一一對應原則都是4byte的表示方法為什么有這么大差異,難道浮點數憑空創造了更多的數據嗎? 非也非也,指數部分代表著浮點數的范圍,尾數部分代表著浮點數的精度;我們從尾數的角度來看,浮點的二進制科學計數法小數點前始終是最高位,這也就意味指數越大,尾數部分所表示的數值越大,其精度越差。所以float與uint表示的數據個數都是一樣的,整形表示的數據是均勻的,而浮點表示的數據在數值比較小的時候精度比較高,而數值比較大的時候就比較低了,同時也說明浮點表示僅僅是一種近似的表示方法,不能精確的表示數值,所以有時候大家在編程的過程中明明向float類型變量賦值了一個準確的數據,仿真一看數據成了一個近似值。 3)浮點與"==" 這個問題也是大家經常討論的,不過還是需要具體情況具體分析,到底浮點數能不能用等于號來進行判斷呢?首先我們看看什么叫相等,相等就是一模一樣,對于計算機而言就是二進制相等,否則我們只能叫近似。前面我們了解到了浮點的存儲形式,如果兩個浮點的三個部分都是相等的(符號位+指數+尾數),那么這個時候這兩個浮點數就是絕對的相等,如果不能達到完全相等就只能使用近似判斷相等,比如我們常用下面的方式來表示: /*************************************** *Author:(公眾號:最后一個bug) ****************************************/ #define FLOAT_EPS (0.000001) //根據需求 #define Float_Equ(a, b) ((fabs((a)-(b)))<(FLOAT_EPS)) 不過對于浮點數相等大家盡量還是減少使用,較多浮點運算控制器都會有不同的處理方式,比如說擴展精度、截取尾數等等,對于代碼的可移植性減弱。 2、玩轉浮點數 前面為大家詳細的介紹了浮點數的理論知識,大家好好溫習一下,這里再為大家分享一下平時用得比較多的浮點數案例,特別是在通信中傳輸浮點數據,數據的組拼容易出現的一些小插曲,同時也是初學者容易忽略的知識點: (代碼走起!) #include #include typedef union _tag_FloatConvert { unsigned char byte[4]; float Result; }uFloatConvert; /***************************************** * Fuction: main * Author ![]() *****************************************/ int main(int argc, char *argv[]) { uFloatConvert unFloatConvert; float fVal = 4.25; int iVal = 0x40880000; float *pfVal = NULL; int *pIVal = NULL; //1)初學組拼數據經常的錯誤 fVal = (float)iVal; printf("*fVal = %.3f\n",fVal); printf("iVal = %d\n",iVal); //2) 正確組拼數據 pfVal = (float*)(&iVal); printf("*pfVal = %.3f\n",*pfVal); //3)采用共聯體進行數據轉化(方便)--大家以后可以封裝成函數 unFloatConvert.byte[0] = 0x00; unFloatConvert.byte[1] = 0x00; unFloatConvert.byte[2] = 0x88; unFloatConvert.byte[3] = 0x40; printf("unFloatConvert.Result = %.3f\n",unFloatConvert.Result); printf("公眾號:最后一個bug\n"); return 0; } 運行結果如下: file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml3492\wps21.png 解析一下: · 1 ) 很多初學者做浮點通信字節接收把接收到的數據組織成整形然后直接強制類型轉化為浮點,然而這樣并不能轉化為原始的浮點數,從上面的運行結果也可以看出來;然而我們采用float指針來進行如上轉換卻得到了正確結果,具體的原因大家通過第一個和第二個打印結果應該就明白了。 · · 2 ) 同時作者這里給出了平時用來轉化浮點的共聯體方法,該方法使用起來比較靈活方便,主要的原理是共聯體共用內存空間,不過要注意大小端問題,相關知識可以看我的往期文章。 · |