IEEE754規(guī)范: 四, 非規(guī)格數(shù), ±infinity, NaN

第一章提到過, ieee754標準中, 浮點數(shù)包含三種狀態(tài)

1. normal number(規(guī)格數(shù))?

2. subnormal number(非規(guī)格數(shù))

3. non-number(特殊數(shù))

本章詳細講解這三種狀態(tài).


一. 首先, 如何區(qū)分這三種狀態(tài)

其實這三種狀態(tài)是通過指數(shù)部分區(qū)分的, 而且很容易區(qū)分.

以32位浮點數(shù)為例, 其內存狀態(tài)分為3部分:

1位符號位? ? ?8位指數(shù)位? ? ?23位尾數(shù)位

其中, 如果8位指數(shù)位全為0, 就代表當前數(shù)是個非規(guī)格數(shù). 或者說, 形如 * 00000000 *********************** 格式的數(shù)就是非規(guī)格數(shù).

如果8位指數(shù)位全為1, 就代表當前數(shù)是個特殊數(shù). 或者說,?形如 *?11111111?*********************** 格式的數(shù)就是特殊數(shù).

如果8位指數(shù)不全為0, 也不全為1(也就是除去以上兩種狀態(tài)外, 剩下的所有狀態(tài)), 這個數(shù)就是規(guī)格數(shù).

隨便幾個例子: * 10101100 ***********************就是一個規(guī)格數(shù)

可見: 非規(guī)格數(shù)特殊數(shù)是兩種特殊狀態(tài), 規(guī)格數(shù)則是非常常見的狀態(tài)

示意圖:

注意下圖把特殊數(shù)分為了兩種狀態(tài), 無窮大NaN:

深入理解計算機系統(tǒng)第三版

二. 這三種狀態(tài)的作用

為什么要把浮點數(shù)分為這三種狀態(tài)呢? 答案當然是有用啊, 而且作用相當直觀:

規(guī)格數(shù): 用于表示最常見的數(shù)值, 比如1.2, 34567, 7.996, 0.2. 但規(guī)格數(shù)不能表示0和非橙靠近0的數(shù).

非規(guī)格數(shù): 用于表示0, 以及非郴沸危靠近0的數(shù), 比如1E-38.

特殊數(shù): 用于表示"無窮"和"NaN":

浮點數(shù)的存儲和計算中會涉及到"無窮"這個概念, 比如:

32位浮點數(shù)的取值范圍是[-3.4*10^{38}, -1.18*10^{-38}] ∪ [1.18*10^{-38}, 3.4 * 10^{38}], 如果你要往里面存儲4e38(這超過了最大的可取值), 32位浮點數(shù)就會在內存中這樣記錄 "你存儲的數(shù)超過了我的最大表示范圍, 那我就記錄你存儲了一個無窮大..."

浮點數(shù)的存儲和計算中還會涉及到"NaN (not a number)"這個概念, 比如:

你要給一個負數(shù)開根號(如 √-1), 但是ieee754標準中的浮點數(shù)卻不知道該怎么進行這個運算, 它就會在內存中這樣記錄 "不知道怎么算, 這不是個數(shù)值, 記錄為NaN"

可見, 這三種狀態(tài)都是非常有用的, 作用也非常直觀, 下面我們一個個來講.


三. 狀態(tài)1: 規(guī)格數(shù)

對于規(guī)格數(shù):?

符號位, 1位: 可正可負

指數(shù)位, 8位: 不全為0, 且不全為1

對于32位浮點數(shù)來說, 規(guī)格數(shù)的指數(shù)位的取值范圍是[1, 254], 偏置bias是127, 所以實際的指數(shù)是:

[1 - 127, 254 - 127], 即 [-126, 127]

注: 關于偏置, 可參見本系列第一章, 此處不再贅述

尾數(shù)位, 23位: 尾數(shù)位前隱藏的整數(shù)部分是1.? 而非 0.

所以尾數(shù)位的取值范圍是[1.00000000000000000000000, 1.11111111111111111111111] (二進制)

換算為10進制為[1,2)

注: 關于尾數(shù)位前隱藏的數(shù),?可參見本系列第一章, 此處不再贅述

規(guī)格數(shù)的局限性: 無法表示 0 和 及其靠近0 的數(shù)

原因很簡單, ieee754浮點數(shù)的求值公式是:

 = ±尾數(shù) * 2^{指數(shù)}

所以可求出32位浮點數(shù)的取值范圍就是:

= ±[1,2) * 2^{[-126,127]}

問題就出現(xiàn)在這里:?

注意尾數(shù)部分: 取值范圍是[1, 2), 始終大于1

注意指數(shù)部分:2^{[-126,127]}, 這個數(shù)始終大于0, 即便2^-167非常小, 但還是大于0

那么: 一個始終大于1的數(shù) * 一個始終大于0的數(shù), 永遠無法等于0

事實上, 1(尾數(shù)最小值) *?2^-167(指數(shù)最小值) =?2^-167.? ? 2^-167就是當前我們能表示的最小值

也就是說: 使用規(guī)格數(shù)時, 我們除了無法表示0, 也無法表示(0, 2^-167)之間的, 靠近0的極小數(shù)...

這就是規(guī)格數(shù)的局限性, 這個局限性將由非規(guī)格數(shù)解決.


補充一點:

其實在本系列的第二章, 我們計算過32位浮點數(shù)的取值范圍:

=(-2 * 2^{127}, -1 * 2^{-126}] ∪ [1 * 2^{-126}, 2 * 2^{127})

所以這里可以畫一個示意圖:

↑ 綠色區(qū)域就是32位浮點數(shù)中規(guī)格數(shù)的取值范圍, 可見它取不到0和靠近0的極小數(shù)

↑ 紅色區(qū)域包含0和靠近0的極小數(shù), 紅色區(qū)域其實是非規(guī)格數(shù)的取值范圍, 見下一節(jié).


四. 狀態(tài)2: 非規(guī)格數(shù)

對于非規(guī)格數(shù):?

符號位, 1位: 可正可負

指數(shù)位, 8位: 全為0

對于32位浮點數(shù)來說, 規(guī)格數(shù)的指數(shù)位全為0, 對應的值也是0. 偏置bias依舊是127, 但:

實際指數(shù)的計算方法是: 實際指數(shù) = 1 - bias = 1 - 127 = -126, 即非規(guī)格數(shù)的實際指數(shù)固定為-126. 注意這是規(guī)定.

其實我們可以發(fā)現(xiàn), 非規(guī)格數(shù)實際指數(shù)的計算方法(實際指數(shù) = 1 - bias), 和規(guī)格數(shù)實際指數(shù)的計算方法(實際指數(shù)? = 指數(shù)位的值 - bias)不同

后文會看到這樣規(guī)定的原因.

尾數(shù)位, 23位: 尾數(shù)位前隱藏的整數(shù)部分是0.?而非?1.

所以尾數(shù)位的取值范圍是[0.00000000000000000000000, 0.11111111111111111111111] (二進制)

換算為10進制為[0,1)

非規(guī)格數(shù)的作用: 表示0和靠近0的數(shù)

那么規(guī)格數(shù)是怎么完成這個任務的呢.?

首先看看非規(guī)格數(shù)是怎么表示0的:

依舊要用到我們的ieee754浮點數(shù)求值公式:

 = ±尾數(shù) * 2^{指數(shù)}

然后, 非規(guī)格數(shù)尾數(shù)的取值范圍是[0, 1), 指數(shù)固定為-126. 這就很簡單了, 讓尾數(shù)取0不就能表示數(shù)值0了:

±0 * 2^{-126} = ±0

可見當尾數(shù)取0時, 通過變更符號位, 我們可以表示出+0-0, IEEE754規(guī)范中也確實存在著這兩種表示0的方式

注: 某些場景下, +0和-0會被認為完全相同, 某些場景下, +0和-0又被認為不完全相同. 這往往取決于具體的編程語言和應用場景, 此處不做討論. 只需知道IEEE754中可以表示+0和-0即可. +0和-0在IEEE754中是兩種內存狀態(tài)(符號位不同)

然后看看非規(guī)格數(shù)是怎么表示接近0的數(shù)的:

準確來說, 我們要看看, 對于32位浮點數(shù), 非規(guī)格數(shù)是怎么表示出=(-1 * 2^{-126}, 1 * 2^{-126})之間的數(shù)的. 也就是如何表示出下圖中的紅色區(qū)域的:

其實也很簡單:

浮點數(shù)求值公式:

 = ±尾數(shù) * 2^{指數(shù)}

然后, 非規(guī)格數(shù)尾數(shù)的取值范圍是[0, 1), 指數(shù)固定為-126.

所以, 非規(guī)格數(shù)的取值范圍就是:

±[1,2) * 2^{-126}

等于:

? (-1*2^{-126}, 0] ∪ [0, 1*2^{-126})

等于:

 (-1*2^{-126}, 1*2^{-126})

這樣就完成了...

現(xiàn)在我們嘗試著把32位浮點數(shù)中的非規(guī)格數(shù)的取值范圍, 和規(guī)格數(shù)的取值范圍拼接在一起

32位浮點數(shù)中, 非規(guī)格數(shù)的取值范圍:

 (-1*2^{-126}, 1*2^{-126})

32位浮點數(shù)中,?規(guī)格數(shù)的取值范圍:

(-2 * 2^{127}, -1 * 2^{-126}] ∪ [1 * 2^{-126}, 2 * 2^{127})

仔細看一下, 啊...非規(guī)格數(shù)的取值范圍, 正好可以卡在規(guī)格數(shù)取值范圍的中間, 現(xiàn)在我們得到了一個完整的取值范圍:

(-2 * 2^{127},  2 * 2^{127})

感覺世界一下子清爽了起來.?

這就是非規(guī)格數(shù)的作用: 用于表示0和靠近0的數(shù), 用于和規(guī)格數(shù)"珠聯(lián)璧合", 形成一個完整的取值范圍.

不過這還沒有完...


五. 非規(guī)格數(shù)補充


1. 逐漸溢出

前文說過, 非規(guī)格數(shù)尾數(shù)的取值范圍是[0, 1), 指數(shù)固定為-126

所以是尾數(shù)的變化在導致非規(guī)格數(shù)的值變大, 舉例:

0 00000000 00000000000000000000001

就比

0 00000000 00000000000000000000000

要大一些

隨著尾數(shù)逐漸增大, 相應的非規(guī)格數(shù)也在不斷增大:

...

0 00000000 11111111111111111111111 這是非規(guī)格數(shù)的最大值

此時尾數(shù)(帶上隱藏的整數(shù)部分0.)其實是0.11111111111111111111111, 是個比1小一點點的數(shù), 不妨記做(1 - ε)

那, 此時非規(guī)格數(shù)的值就是?(1-ε) * 2^{-126}

好, 我們再往前前進一格, 此時會進入規(guī)格數(shù)的范圍:

0 00000001 00000000000000000000000

這是個規(guī)格數(shù),?

其尾數(shù)位的值: 其實隱藏了 1.?或者說, 此時真正的尾數(shù)應該是1.00000000000000000000000 , 也就是1

其指數(shù)位的值: 是1, 則實際指數(shù)應該是1 - bias = 1-127 = -126

所以這個規(guī)格數(shù)的值就是:?1 * 2^{-126}, 這是規(guī)格數(shù)的最小值.

注意到?jīng)]有: 非規(guī)格數(shù)的最大值是:?(1-ε) * 2^{-126}

規(guī)格數(shù)的最小值是:?1 * 2^{-126}

兩者之間實現(xiàn)了非常平滑的過度, 非規(guī)格數(shù)的最大值非常緊密的連接上了規(guī)格數(shù)的最小值

非規(guī)格數(shù) "一點點逐漸變大,? 最后其最大值平穩(wěn)的銜接上規(guī)格數(shù)的最小值", 這種特性在ieee754中被叫做逐漸溢出(gradual underflow)

明白了這一點, 就很容易想通:?

① 為什么規(guī)定非規(guī)格數(shù)的尾數(shù)前隱藏的整數(shù)部分是?0.??而規(guī)格數(shù)尾數(shù)前隱藏的整數(shù)部分是1.?

② 為什么非規(guī)格數(shù)的真實指數(shù)的計算公式是 1 - bias, 而規(guī)格數(shù)的真實指數(shù)的計算公式是 指數(shù)部分的值 - bias

仔細思考一下, 就是這些設計實現(xiàn)了逐漸溢出這種特性.?

↑ 關于第①點: 這使得非規(guī)格數(shù)的尾數(shù)取值范圍是[0,1), 而規(guī)格數(shù)的尾數(shù)取值范圍是[1,2), 兩者平滑的銜接在了一起

↑ 關于第②點:?這使得對于32位浮點數(shù)來說, 非規(guī)格數(shù)的真實指數(shù)固定為-126, 而規(guī)格數(shù)的指數(shù)是[-126, 127], 兩者也平滑的銜接在了一起...


2. 密集分布

第三章中我們說過: 如果把ieee754浮點數(shù)想象成一個表盤的話, 那表盤上的藍點是越來越稀疏的. 或者說越靠近0越密集.

不過當時僅討論了規(guī)格數(shù)的分布情況, 那非規(guī)格數(shù)呢.

答案是, 非規(guī)格數(shù)的藍點分布間隔, 和規(guī)格數(shù)中藍點最密集的區(qū)域(也就是最靠近0的區(qū)域)一致, 可以驗證一下:

非規(guī)格數(shù): 范圍是 [0, 1*2^{-126}),?在這個范圍中分布了2^23個藍點, 則藍點間的間隔是2^{-126} / 2^{23} = 2^{-149}

規(guī)格數(shù)中藍點最密集的區(qū)域, 也就是最靠近0的區(qū)域是:?[2^{-126}, 2^{-125}], 在這個范圍中分布了2^23個藍點, 則藍點間的間隔是(2^{-125}-2^{-126}) / 2^{23} = 2 ^ {-126}/2^{23} = 2^{-149}

所以, 即便把非規(guī)格數(shù)與規(guī)格數(shù)放在一起審視, ieee754浮點數(shù)表盤上的藍點依舊是越靠近0越密集, 越靠近∞越稀疏

深入理解計算機系統(tǒng)第三版, 浮點數(shù)密度示意圖. 中間部分密密麻麻的都是非規(guī)格數(shù)

下面是在c語言中的測試結果:

六. 狀態(tài)3: 特殊數(shù)

特殊數(shù)分為兩種: 無窮和NaN

1. 先說無窮

理解了非規(guī)格數(shù), 再理解無窮就很簡單了, 兩者有很多相似之處:

對于無窮:?

符號位, 1位: 可正可負

指數(shù)位, 8位: 全為1

尾數(shù)位, 23位: 全部為0

當內存位于上述狀態(tài)時, 就表示無窮(infinity)

具體寫出來就是: * 11111111 00000000000000000000000 用于表示無窮(infinity)

其中符號位可正可負, 分別記做+infinity和-infinity


以32位浮點數(shù)為例, 其規(guī)格數(shù)的取值范圍是:?

(-2 * 2^{127}, -1 * 2^{-126}] ∪ [1 * 2^{-126}, 2 * 2^{127})

當要存儲的數(shù)大于規(guī)格數(shù)取值范圍的最大值時, 就會被記做+infinity, 比如2^128, 剛剛超過規(guī)格數(shù)的取值范圍的最大值, 就會被記做+infinity

當要存儲的數(shù)小于規(guī)格數(shù)取值范圍的最小值時, 就會被記做-infinity, 比如-2^128, 剛剛小于規(guī)格數(shù)的取值范圍的最小值, 就會被記做-infinity

需要注意的是: 所有+infinity的內存狀態(tài)都是0 11111111?00000000000000000000000, 不會有任何變動

2^128對應的內存狀態(tài)是0 11111111?00000000000000000000000

2^123456789對應的內存狀態(tài)還是0 11111111?00000000000000000000000

同理, -infinity的內存狀態(tài)都是1?11111111?00000000000000000000000

此外: 就像非規(guī)格數(shù)的最大值可以和規(guī)格數(shù)的最小值平穩(wěn)銜接一樣, 規(guī)格數(shù)的最大值也可以和+infinity平穩(wěn)銜接:

規(guī)格數(shù)的最大值是: 0 11111110 11111111111111111111111

尾數(shù)位其實是1.11111111111111111111111, 非常接近2, 不妨記做2-ε

指數(shù)是127

所以最大值是:?(2-ε)*2^{127}

+infinity的內存狀態(tài)則是: 0 11111111?00000000000000000000000

尾數(shù)其實是:?1.00000000000000000000000, 等于1

指數(shù)是128

所以+infinity的內存狀態(tài)對應的值是:?2^{128}

可見規(guī)格數(shù)的最大值也能和+infinity平穩(wěn)銜接. -infinity同理.

現(xiàn)在我們就集齊了整個數(shù)軸:


↑ 而且各個節(jié)點都能平穩(wěn)的銜接在一起


2. NaN

NaN則更簡單, 前面說過, 如果計算出來的值不是一個數(shù)值, 則記錄為NaN

NaN的內存狀態(tài)是:?

符號位, 1位: 可正可負

指數(shù)位, 8位: 全為1

尾數(shù)位, 23位: 不全為0即可


僅僅是一種特殊狀態(tài)標記而已.

需要注意的是, 根據(jù)wiki, 沒有+NaN或-NaN這種說法, 統(tǒng)稱為NaN

七. 總結

本章介紹了IEEE754規(guī)范中的非規(guī)格數(shù), 特殊值(±infinity, NaN), 包括它們的內存狀態(tài), 作用, 工作原理等.

下一章會先偏離一下主線, 補充一些之前沒有提到的瑣碎知識點.?

下一章再見吧~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(liá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
  • 正文 為了忘掉前任陋气,我火速辦了婚禮,結果婚禮上引润,老公的妹妹穿的比我還像新娘巩趁。我一直安慰自己,他們只是感情好淳附,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布议慰。 她就那樣靜靜地躺著,像睡著了一般奴曙。 火紅的嫁衣襯著肌膚如雪别凹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天洽糟,我揣著相機與錄音炉菲,去河邊找鬼。 笑死坤溃,一個胖子當著我的面吹牛拍霜,可吹牛的內容都是我干的。 我是一名探鬼主播薪介,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼祠饺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了汁政?” 一聲冷哼從身側響起道偷,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤缀旁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后试疙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诵棵,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年祝旷,在試婚紗的時候發(fā)現(xiàn)自己被綠了履澳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡怀跛,死狀恐怖距贷,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情吻谋,我是刑警寧澤忠蝗,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站漓拾,受9級特大地震影響阁最,放射性物質發(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

推薦閱讀更多精彩內容