最近在項(xiàng)目中遇到了浮點(diǎn)數(shù)相加結(jié)果顯示異常的問題妖异,如下圖所示
理論上來說惋戏,d應(yīng)該是等于0的,為什么結(jié)果和預(yù)期的不一樣呢他膳,所以抽空研究了一下float
原理
浮點(diǎn)數(shù)的表示有一個 IEEE 的標(biāo)準(zhǔn)响逢,它定義了兩個基本的格式。
- 用 32 比特表示單精度的浮點(diǎn)數(shù)棕孙,即 float 或者 float32 類型舔亭。
- 用 64 比特表示雙精度的浮點(diǎn)數(shù),即 double 或者 float64 類型蟀俊。
這兩種原理差不多钦铺,所以只要看單精度類型的就可以了
第一部分是一個符號位,用來表示是正數(shù)還是負(fù)數(shù)肢预,用 s 來表示矛洞,所有的浮點(diǎn)數(shù)都是有符號的。
接下來是一個 8 個比特組成的指數(shù)位烫映。一般用 e 來表示沼本。8 個比特能夠表示的整數(shù)空間噩峦,就是 0~255。我們在這里用 1~254 映射到 -126~127 這 254 個有正有負(fù)的數(shù)上抽兆。浮點(diǎn)數(shù)希望能夠表示很小的數(shù)壕探,所以指數(shù)位也會有負(fù)數(shù)。
最后郊丛,是一個 23 個比特組成的有效數(shù)位。我們用 f 來表示瞧筛。綜合科學(xué)計(jì)數(shù)法厉熟,我們的浮點(diǎn)數(shù)就可以表示成下面這樣: (?1)s × 1.f × 2e
以 0.5 為例子。0.5 的符號為 s 應(yīng)該是 0较幌,f 應(yīng)該是 0揍瑟,而 e 應(yīng)該是 -1,也就是0.5 = (?1)0 × 1.0 × 2?1 = 0.5乍炉,對應(yīng)的浮點(diǎn)數(shù)表示绢片,就是 32 個比特。如下圖所示s=0岛琼,e=2?1底循,需要注意,e 表示從 -126 到 127 個槐瑞,-1 是其中的第 126 個數(shù)熙涤,這里的 e 如果用整數(shù)表示,就是 26 + 25 + 24 + 23 + 22 + 21 = 126困檩,1.f=1.0祠挫。
為什么會不精確
10進(jìn)制小數(shù)轉(zhuǎn)換為2進(jìn)制的方法:乘2,然后取整(整數(shù)部分最大為1悼沿,最小為0)等舔,小數(shù)部分繼續(xù)乘2,取整糟趾,直到小數(shù)部分0為止慌植。
以0.1為例,轉(zhuǎn)化如下
乘2 | 整數(shù)部分 | 二進(jìn)制表示 | |
---|---|---|---|
1 | 0.1*2=0.2 | 0 | 0 |
2 | 0.2*2=0.4 | 0 | 0 |
3 | 0.4*2=0.8 | 0 | 0 |
4 | 0.8*2=1.6 | 1 | 1 |
5 | 0.6*2=1.2 | 1 | 1 |
6 | 0.2*2=0.4 | 0 | 0 |
從第6行開始就會重復(fù)2~5行的運(yùn)算拉讯,0.1就會變成一個無限循環(huán)的小數(shù):0.000110011……涤浇,這里的0011無限循環(huán)。浮點(diǎn)數(shù)其實(shí)是用二進(jìn)制的科學(xué)計(jì)數(shù)法來表示的魔慷,所以我們可以把小數(shù)點(diǎn)左移三位只锭,這個數(shù)就變成了:1.10011001100110011…× 2-4。
這種科學(xué)計(jì)數(shù)法對應(yīng)打上面的格式為:符號位 s = 0院尔,對應(yīng)的有效位 f=1001100110011…蜻展。因?yàn)?f 最長只有 23 位喉誊,那這里“0011”無限循環(huán),最多到 23 位就截止了纵顾。于是伍茄,f=10011001100110011001100。對應(yīng)的指數(shù)為 e施逾,代表的應(yīng)該是 -4敷矫。其對應(yīng)的是123,轉(zhuǎn)化成二進(jìn)制就是 01111011汉额。那么最終的二進(jìn)制表示為:001111011 10011001100110011001100
現(xiàn)在再將二進(jìn)制浮點(diǎn)數(shù)轉(zhuǎn)化為十進(jìn)制曹仗,結(jié)果為0.0999999940395
知道這些有什么用?
在討論這個之前蠕搜,先看一下浮點(diǎn)數(shù)如何相加怎茫。首先要把兩個的指數(shù)位對齊,也就是把指數(shù)位都統(tǒng)一成兩個其中較大的那個妓灌,那么指數(shù)位較小的數(shù)轨蛤,需要進(jìn)行有效位右移,在右移的過程中虫埂,最右側(cè)的有效位就被丟棄掉了祥山。這會導(dǎo)致對應(yīng)的指數(shù)位較小的數(shù),在加法發(fā)生之前掉伏,就丟失精度枪蘑。如果兩個數(shù)的指數(shù)位差出 23 位,較小的數(shù)右移 24 位之后岖免,所有的有效位就都丟失了岳颇。這也就意味著,雖然浮點(diǎn)數(shù)可以表示上到 3.40×1038颅湘,下到 1.17×10?38 這樣的數(shù)值范圍话侧。但是在實(shí)際計(jì)算的時(shí)候,只要兩個數(shù)闯参,差出 224瞻鹏,也就是 16777216倍,那這兩個數(shù)相加之后鹿寨,結(jié)果完全不會變化新博。如下圖所示
參考文章:深入淺出計(jì)算機(jī)組成原理