最近進(jìn)行代碼的review過(guò)程中看到同事在代碼中直接拿浮點(diǎn)數(shù)相等來(lái)作為條件,其他同事提醒他的時(shí)候,他還迷迷糊糊不知道為什么,所以就有了今天這篇文章。
1、浮點(diǎn)數(shù)據(jù)的不均勻
我們經(jīng)常會(huì)談到浮點(diǎn)數(shù)的精度問(wèn)題,float-單精度,double-雙精度,double類型相比f(wàn)loat類型精度更高,相應(yīng)的需要的內(nèi)存字節(jié)個(gè)數(shù)也越多,談到精度的問(wèn)題,其實(shí)也就說(shuō)明這種數(shù)據(jù)類型并不能夠連續(xù)的標(biāo)識(shí)任何的點(diǎn),整形數(shù)就不用說(shuō)了,小數(shù)部分直接不能標(biāo)識(shí)。
毒王這篇文章基本上可以從浮點(diǎn)數(shù)的存儲(chǔ)到表意來(lái)較好的認(rèn)識(shí)浮點(diǎn)數(shù)數(shù)據(jù)類型,但是中間部分對(duì)于浮點(diǎn)數(shù)精度部分的介紹并不是很形象,所以今天再詳細(xì)一點(diǎn)說(shuō)明一下。
首先我們要認(rèn)識(shí)到通常float類型的變量占據(jù)四個(gè)字節(jié),而uint32_t的整形類型也是占據(jù)四個(gè)字節(jié),既然都是四個(gè)字節(jié),那他們所能表示的不同數(shù)據(jù)個(gè)數(shù)是一樣的。
如果不太理解,可以把float看成4個(gè)bit,uint32_t也是4個(gè)bit,那么他們不管經(jīng)過(guò)什么變換,每個(gè)數(shù)據(jù)類型都只能夠標(biāo)識(shí)16個(gè)數(shù)。
好,如下圖以4字節(jié)float的數(shù)據(jù)存儲(chǔ)模型所示:
4個(gè)字節(jié)的浮點(diǎn)數(shù),不像無(wú)符號(hào)整形所有的bit都是數(shù)據(jù)區(qū),并且以每個(gè)數(shù)據(jù)之間相差1均勻分布,而浮點(diǎn)數(shù)把這4個(gè)字節(jié)分為了不同的區(qū)來(lái)起到不同的作用,從而用另外一種方式表達(dá)數(shù)據(jù)。
其指數(shù)部分越大,表示的數(shù)據(jù)就越大,但是尾數(shù)部分只能表示到23位,這樣的話導(dǎo)致數(shù)據(jù)的精度就越差,如果不太理解可以用一個(gè)較大的數(shù)通過(guò)上面的轉(zhuǎn)換方式進(jìn)行換算,便能理解。
所以同樣是4個(gè)字節(jié),根據(jù)浮點(diǎn)數(shù)的表示,越接近0就越稠密,越遠(yuǎn)離0就越稀疏,呈現(xiàn)一種不均勻的數(shù)據(jù)排列狀態(tài),如上圖所示,同樣它也也不能標(biāo)識(shí)實(shí)軸上任意的點(diǎn)。
2、驗(yàn)證一下不均勻
好了,講了這么多理論,多多少少得來(lái)點(diǎn)程序驗(yàn)證一下:
看看上面的代碼,這還用說(shuō),肯定這兩個(gè)數(shù)相等呀,相減也等于0,然而看一下輸出結(jié)果:
結(jié)果并不相等,并且相差還不少。
其結(jié)果也就說(shuō)明了浮點(diǎn)數(shù)在大數(shù)的標(biāo)識(shí)精度不好,只能近似標(biāo)識(shí),同時(shí)也說(shuō)明了為什么一般不使用浮點(diǎn)數(shù)相等來(lái)進(jìn)行判斷的原因。
這也是為什么有時(shí)候明明我們采用直接編碼用準(zhǔn)確的浮點(diǎn)數(shù),到了浮點(diǎn)數(shù)變量里面卻損失了精度,因?yàn)?個(gè)字節(jié)的float標(biāo)識(shí)不了,只能近似處理。
3、非要判斷相等
由于有些應(yīng)用非要使用浮點(diǎn)數(shù)進(jìn)行相等的處理,我們不應(yīng)該直接使用浮點(diǎn)數(shù)進(jìn)行等于號(hào)的判斷,而是要在一定的誤差和精度范圍內(nèi)進(jìn)行滿足。
如上圖所示代碼是比較常用的處理辦法,在往期的文章中,bug菌沒(méi)有詳細(xì)的講解這個(gè)誤差宏的定義,前面了解到當(dāng)數(shù)據(jù)比較大的時(shí)候相鄰的差值會(huì)比較大,這樣就存在兩個(gè)浮點(diǎn)數(shù)的差值大于所設(shè)置的誤差范圍而無(wú)法判斷相等。
所以這樣的處理辦法來(lái)判斷浮點(diǎn)數(shù)近似相等會(huì)存在一些局限性。
那有沒(méi)有相對(duì)更好一點(diǎn)的辦法呢?
當(dāng)然是有的,不然接下來(lái)沒(méi)得寫(xiě)了。
還是要從浮點(diǎn)數(shù)的存儲(chǔ)和標(biāo)識(shí)出發(fā)來(lái)處理該問(wèn)題,既然浮點(diǎn)數(shù)天然就存在一定的誤差,而有時(shí)候計(jì)算又無(wú)法獲得唯一的數(shù)值,如下圖所示,浮點(diǎn)數(shù)計(jì)算出來(lái)的實(shí)軸上的值都會(huì)因?yàn)楦↑c(diǎn)數(shù)無(wú)法存儲(chǔ)標(biāo)識(shí)而近似到其相鄰的可以標(biāo)識(shí)的數(shù)值上。
從浮點(diǎn)的存儲(chǔ)模型來(lái)看,指數(shù)部分代表著浮點(diǎn)數(shù)的范圍,尾數(shù)部分代表著浮點(diǎn)數(shù)的精度,那么尾數(shù)的最后一位其實(shí)就表示了浮點(diǎn)數(shù)的當(dāng)前數(shù)值附近的精度。
于是對(duì)浮點(diǎn)的近似相等進(jìn)行了算法上的修改,如下代碼所示:
解釋一下 :
如果直接相等,說(shuō)明浮點(diǎn)數(shù)各數(shù)據(jù)位都相等;而如果不相等可能相鄰,于是強(qiáng)制轉(zhuǎn)化為整形,比較尾數(shù)最后一位是否不同。
這里使用一個(gè)小技巧,采用異或的處理辦法,如果其他位都相同,而最后一位不同,結(jié)果就等于1,認(rèn)為兩個(gè)浮點(diǎn)數(shù)近似相等。
本文到此結(jié)束,我相信大家應(yīng)該對(duì)浮點(diǎn)數(shù)有了一個(gè)更加深入的了解,面對(duì)一些問(wèn)題心中也會(huì)有一些答案,比如浮點(diǎn)數(shù)為什么不能作為switch的參數(shù),也是同樣的原因。
但總的來(lái)說(shuō)還是建議大家不要判斷浮點(diǎn)數(shù)相等,非要用也要特別小心。
審核編輯:劉清
-
Switch
+關(guān)注
關(guān)注
1文章
535瀏覽量
59114 -
數(shù)據(jù)存儲(chǔ)
+關(guān)注
關(guān)注
5文章
992瀏覽量
51551 -
浮點(diǎn)數(shù)
+關(guān)注
關(guān)注
0文章
61瀏覽量
16052
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
官方例程modbus slave rtu,浮點(diǎn)數(shù)精度不對(duì)是怎么回事?
浮點(diǎn)數(shù)的表示方法

浮點(diǎn)數(shù)常用的編碼方法

modbus 如何讀取浮點(diǎn)數(shù)
Xilinx怎么定點(diǎn)數(shù)轉(zhuǎn)浮點(diǎn)數(shù)

什么是浮點(diǎn)數(shù)?浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)

什么是浮點(diǎn)數(shù)
定點(diǎn)數(shù)和浮點(diǎn)數(shù)的概念 浮點(diǎn)數(shù)二進(jìn)制序列與指數(shù)表達(dá)式之間的轉(zhuǎn)化

單精度和雙精度浮點(diǎn)數(shù)的區(qū)別
單精度和雙精度浮點(diǎn)數(shù)的區(qū)別
modbus浮點(diǎn)數(shù)怎么讀取
一文帶你秒懂IEEE 754浮點(diǎn)數(shù)

評(píng)論