IEEE 754規(guī)范: 五, 一些補充

本章討論一些零碎的話題.

一. 其他導(dǎo)致浮點數(shù)存儲不精確的原因

在第三章中我們提到過, 數(shù)學中的小數(shù)是連續(xù)的, 而計算機中的小數(shù)(準確來說是ieee754標準中的小數(shù))是離散的.

這就會引發(fā)精度問題: 圖中的綠色指針只能指向藍點, 不能指向藍點之間的數(shù). 比如上面最右邊的圖, 綠色指針其實無法指向0.3, 當你想要指向0.3時, 實際上會被舍入為0.234, 即舍入到離它最近的藍點對應(yīng)的值.


而除此之外, 進制問題也會導(dǎo)致IEEE754浮點數(shù)存儲不精確

簡單來說就是: 有限長度編碼下, 每種進制都有他們不能精確表示的值

比如: 10進制不能精確的表示1/3 (0.3333333.....)

十進制可以精確表示1/5 (0.2), 但二進制無法精確表示1/5

不能精確表示時,只能進行近似. 編碼長度越長止后,近似程度越高

舉例: 下圖嘗試用二進制表示0.2, 可以發(fā)現(xiàn), 只能近似表示...

當你把一個十進制數(shù)存儲到計算機中時, 實際上存儲的是該數(shù)的二進制表示.

所以, 當你寫入如下代碼時:

float f = 0.2;

雖然0.2(十進制)遠遠沒有到達32位浮點數(shù)的精度上限(7位精度), 但計算機其實無法精確地存儲該數(shù)值, 因為0.2(十進制)無法使用二進制格式精確表示.

此時變量f對應(yīng)的內(nèi)存狀態(tài)是這樣的:

↑ 你鍵入的是0.2

↑ 內(nèi)存中實際存儲的是0.20000000298023223876953125

可以在c語言中驗證一下:

可見十進制的0.2無法用二進制精確表示, 但十進制的0.5卻可以用二進制精確表示.

二. 二進制的小數(shù)形式

有些同學可能會納悶, 二進制為什么會有小數(shù)形式? 我常見的二進制都是整數(shù)形式啊, 比如十進制的9, 表示為二進制是1001, 怎么會有 1001.101 這種二進制的小數(shù)格式呢.

其實對于程序員來說, 這里確實比如容易讓人困惑, 比如win10自帶的計算機, 就不支持二進制小數(shù):

二進制模式下, 小數(shù)點是不能用的

許多編程語言, 比如js, 也不支持直接使用二進制小數(shù):

支持二進制整數(shù), 但不支持二進制小數(shù)

但和十進制一樣, 二進制其實也有小數(shù)形式, 而且很容易理解:

比如對于十進制數(shù) 78.23

十位: 7, 表示 7 * 10^1 = 70

個位: 8, 表示 8 * 10^0 = 8

十分位: 2, 表示2/10, 或說表示2 * 10^{-1}

百分位: 3, 表示3/100, 或說表示3 * 10^{-2}

這個十進制所表示的值是: 70 + 8 + 2/10 + 3/100


二進制數(shù)也是同理的:

比如對于二進制數(shù) 10.11

第一位: 1, 表示1 * 2^1 = 2

第二位: 0, 表示0 * 2^0 = 0

第三位: 1, 表示1 * 2^-1 = 0.5

第四位: 1, 表示1 * 2^-2 = 0.25

所以這個二進制表示的值, 其實就是十進制的2 + 0 + 0.5 + 0.25 = 2.75


這里比較有意思的一點是:

十進制小數(shù)點后面的那一位(也就是十分位), 對應(yīng)的權(quán)是1/10, 也就是0.1

即, 對于十進制數(shù)3.4, 這個4對應(yīng)的值是: 4 * 權(quán)? =? 4 * 0.1 = 0.4

而二進制小數(shù)點后面的一位, 對應(yīng)的權(quán)是1/2, 也就是十進制的0.5

所以對于二進制數(shù)0.1, 這個1對應(yīng)的值是: 1 * 0.5 = 0.5, 所以二進制的0.1, 其實等于十進制的0.5

這讓我想起來一個腦筋急轉(zhuǎn)彎, 問: 什么時候 1.11.3 要大?

答: 當1.1是個二進制數(shù), 而1.3是個十進制數(shù)的時候...


事實上: 對于小數(shù)點之后的位, 二進制的位權(quán)始終比十進制的位權(quán)要大, 舉例:

十進制數(shù): 小數(shù)點之后的位權(quán)依次是: 1/10,? 1/100,? 1/1000...

二進制數(shù): 小數(shù)點之后的位權(quán)依次是: 1/2,? 1/4,? 1/8...? 相應(yīng)位的權(quán)始終比↑十進制的要大

所以會出現(xiàn)這種現(xiàn)象

二進制:? ? ? ? ? ? ? ? 1.000001, 小數(shù)點后面的數(shù)看起來已經(jīng)很小很小了

對應(yīng)的十進制是:? 1.015625, 小數(shù)點后面的數(shù)其實還挺大...


在IEEE765標準中, 我們會經(jīng)常和二進制小數(shù)打交道, 所以這里補充一下相關(guān)知識.


三. 關(guān)于32位浮點數(shù), 一些不太正確的認知

1. 32位浮點數(shù)能存儲很大的整數(shù)

這是32位浮點數(shù)的取值范圍:

[-3.4*10^{38}, -1.18*10^{-38}] ∪ [1.18*10^{-38}, 3.4 * 10^{38}]

當我第一次看到這個取值范圍時, 我是很驚訝的, 怎么這么大?

一個浮點數(shù), 占用32字節(jié), 竟然能存儲下約±340000000000000000000000000000000000000這么大的數(shù)

相比之下, 一個同樣32字節(jié)的long類型, 存儲范圍只有約±2147483647

那我為啥還要用long類型...

...

一路學習到現(xiàn)在, 倒是可以繞過這個彎兒了, 那就是:

32位浮點型確實最大可以存儲到3.4 * 10^{38}這么大的數(shù), 但精度很低

第三章中我們說過, 32位浮點數(shù)表盤中的藍點會越來越稀疏:


等到了3.4 * 10^{38}這么大的數(shù)時, 其實藍點已經(jīng)稀疏的不成樣子了, 基本是不可用狀態(tài)

根據(jù)wiki中給出的間隔, 對于1.70141e38 到 3.40282e38范圍中的數(shù), 間隔是2.02824e31

也就是說, 大體上: 32位浮點數(shù)中, 能精確存儲1.70141e38

但無法精確存儲1.70141e38 + 1

也無法精確存儲1.70141e38 + 2,

也無法精確存儲1.70141e38 + 100000000000

...

下一個能精確存儲的數(shù)是: 1.70141e38 + 20282400000000000000000000000000 (即加上間隔)

這個精度基本上是不可用的.

事實上, 如果你要用float存儲整數(shù)的話, 最多只能精確存儲到 16777216

再大的話, 間隔就會變?yōu)?, 就不適合用來存儲整數(shù)了:

此時再回過頭來看看同為 32位 的long類型, 能精確存儲的整數(shù)范圍

約是: ±2147483647

比:? ? ±16777216? ? 大多了

所以存儲大整數(shù)還是用long類型吧

總結(jié): 32位浮點數(shù)只是有能力存儲到3.4 * 10^{38}, 實際上存儲的數(shù)過大會導(dǎo)致精度過低, 基本上不可用. 用32位浮點數(shù)存儲整數(shù)時, 只適用存儲±16777216之間的整數(shù).


2. 32位浮點數(shù)能存儲很精確的小數(shù)

這是32位浮點數(shù)的取值范圍:

[-3.4*10^{38}, -1.18*10^{-38}] ∪ [1.18*10^{-38}, 3.4 * 10^{38}]

看起來好像能存儲1.18*10^{-38}這么精確的小數(shù)...

但其實和存儲整數(shù)一樣, 32位浮點數(shù)只是有能力存儲到1.18*10^{-38}這么小的小數(shù)而已...

事實上在第三章中我們詳細講解過: 32位浮點數(shù)的精確度是7位有效數(shù).

即如果你要存儲的數(shù) 整數(shù)部分 + 小數(shù)部分 放在一起超過了 7 位, 32位浮點數(shù)就不能精確存儲了

比如, 32位浮點數(shù)就不能精確存儲我們常背的部分圓周率

32位浮點數(shù)倒是可以存儲常見的月工資, 比如 5078.65, 或 12665.73. 但如果要存儲年工資, 或把工資存儲到3位小數(shù), 32位浮點數(shù)就不一定夠用了...

所以, 雖然32位浮點數(shù)的取值范圍看起來很大, 足足有:

[-3.4*10^{38}, -1.18*10^{-38}] ∪ [1.18*10^{-38}, 3.4 * 10^{38}]

但其實32位浮點數(shù)只適合存儲常見數(shù)據(jù)...

感性地去認知的話, float(也就是32位浮點數(shù))類型其實和int類型有些相似: int用于存儲最常用, 最自然的整數(shù). float則用于存儲最常用, 最自然的浮點數(shù)...編程時, 如果要存儲的數(shù)很大或精度很高(相對來說现恼,這些數(shù)往往不怎么常用或不怎么自然), 就要考慮改用long或double.

精確來說的話, 就是不要被32位浮點數(shù)駭人的取值范圍嚇到. 而是記住事實上它只能存儲7位有效數(shù)就行了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載稚机,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末刽酱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子润文,更是在濱河造成了極大的恐慌殿怜,老刑警劉巖典蝌,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件头谜,死亡現(xiàn)場離奇詭異,居然都是意外死亡柱告,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門葵袭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甲脏,“玉大人,你說我怎么就攤上這事块请。” “怎么了贸弥?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵海渊,是天一觀的道長哲鸳。 經(jīng)常有香客問我盔憨,道長,這世上最難降的妖魔是什么郁岩? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任问慎,我火速辦了婚禮,結(jié)果婚禮上如叼,老公的妹妹穿的比我還像新娘。我一直安慰自己踊沸,他們只是感情好挖腰,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猴仑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疾渣。 梳的紋絲不亂的頭發(fā)上崖飘,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天,我揣著相機與錄音朱浴,去河邊找鬼。 笑死项乒,一個胖子當著我的面吹牛梁沧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼栓辜,長吁一口氣:“原來是場噩夢啊……” “哼垛孔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起周荐,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤羡藐,失蹤者是張志新(化名)和其女友劉穎悯许,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體先壕,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年集绰,在試婚紗的時候發(fā)現(xiàn)自己被綠了谆棺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡碍岔,死狀恐怖朵夏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仰猖,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布鸵赫,位于F島的核電站爆捞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盗温,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卖局。 院中可真熱鬧,春花似錦砚偶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仲锄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镣奋,已是汗流浹背怀愧。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芯义,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓温赔,卻偏偏與公主長得像鬼癣,于是被迫代替她去往敵國和親陶贼。 傳聞我的和親對象是個殘疾皇子待秃,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361