浮點數,二進位浮點數,IEEE 754 二進位浮點數介紹
浮點數
浮點數是一種小數表示方式,其中“浮點”的含義為小數點是浮動變化的,這通常對應著指數的改變,因為浮點數儲存數值科學運算質的相關資訊,包括指數,尾數等項目。
為何使用浮點數?
總的來說,浮點數是一種平衡方案,兼顧了擴大數值描述範圍和節省儲存空間,因為通過指數可以表示更趨向於0
或無限大的數值,而其自身占用的空間又很小。
假設浮點數采用了十進位,那麽描述0.000000000001234
小數點後面大量的0
,只需儲存其科學運算質1.234×10⁻¹²
中的指數-12
,而不是為每個0
分配真實的儲存空間。
二進位浮點數
目前的浮點數硬體普遍采用二進位,這是由於 IEEE 754-1985 標準的流行,該標準僅規定了浮點數的二進位儲存格式。而在 IEEE 754-1985 出現之前,浮點數的格式並不統一,有些硬體支援十進位甚至三進位格式的浮點數。
雖然後來的 IEEE 754-2008 加入了浮點數的十進位儲存格式,但改變不會輕易發生,因為這對於硬體廠商意味著風險。
不同進位浮點數所儲存的指數和尾數
理所當然的,對於不同進位的浮點數,其儲存的指數和尾數資訊,應來自於對應進位的科學運算質,否則會給計算帶來不必要的麻煩。
比如,將小數12.3
轉換為采用十進位的浮點數,如果確定使用科學運算質1.23×10¹
,那麽指數應該可以換算為十進位的1
,尾數應該可以換算為十進位的1.23
。
二進位浮點數不準確
事實上因為進位的轉換問題,采用二進位的浮點數具有不準確性,你可以檢視為何二進位浮點數不準確?IEEE 754 二進位浮點數拾入規則一節了解相關資訊。
IEEE 754 二進位浮點數
同樣是依賴科學運算質和二進位格式,不同的設計方案可能導致浮點數的表現大相徑庭,IEEE 754 定義了二進位浮點數符號,指數,尾數的格式,他們依次排列並占用 4,8 或更多個位元組的儲存空間。
IEEE 754 二進位浮點數符號格式
符號占用 1 個 bit 的儲存空間,為1
時表示浮點數是一個負數,為0
時表示浮點數是一個非負數。
IEEE 754 二進位浮點數指數格式
指數在 4/8 位元組浮點數中占用 8/11 個 bit 的儲存空間,指數在儲存之前需要計算為指數編碼值,計算方法為科學運算質中的指數真實值加上指數位移值。指數位移值的計算公式是2ⁿ⁻¹-1
,n
為指數占用的 bit 個數,4/8 位元組浮點數的指數位移值為127
/1023
。
使用指數編碼值是為了方便表示指數為負的情況,假設在 4 位元組的浮點數中,指數儲存的二進位內容為01111011
(對應十進位數值為123
),那麽指數編碼值就是123
,指數真實值就是123-127
,即-4
。
此外,除了參與正常運算,指數編碼值還用於判斷特殊值或特殊格式是否成立。
IEEE 754 二進位浮點數尾數格式
尾數在 4/8 位元組浮點數中占用 23/52 個 bit 的儲存空間,當尾數不等價於0
並且指數編碼值對應的十進位數值大於等於0
小於2ⁿ⁻¹-1
(其中n
表示指數在儲存空間中占用的 bit 個數)時,尾數最高位將被隱藏,不會真正儲存在浮點數中。這種做法使得尾數節省出 1 個 bit 的儲存空間,而且不會影響運算,因為根據指數編碼值可以得知被隱藏的尾數最高位是0
還是1
。
十進位的0.5
對應的二進位科學運算質為1×10⁻¹
(其中的數位0
,1
均為二進位,10
對應了十進位的2
),尾數最高位1
被省去後,其在 4 位元組浮點數中的儲存內容將是00000000000000000000000
(僅尾數部分)。
IEEE 754 二進位正規和次正規浮點數
如果指數編碼值對應的十進位數值大於0
小於2ⁿ⁻¹-1
(其中n
表示指數在儲存空間中占用的 bit 個數),則浮點數屬於正規浮點數,被隱藏的尾數最高位為1
。正規浮點數是最為常見的,其二進位科學運算質中的尾數最高位總是為1
。
如果指數編碼值等價於0
,尾數儲存的二進位內容不等價於0
,則浮點數屬於次正規浮點數,被隱藏的尾數最高位為0
,指數真實值由0-(2ⁿ⁻¹-1)
改為0-(2ⁿ⁻¹-1)+1
(其中n
表示指數在儲存空間中占用的 bit 個數)。
IEEE 754 二進位次正規浮點數的作用是什麽?
次正規浮點數用於表示比正規浮點數更趨近於0
的小數,因為其隱藏的尾數最高位為0
,對應的二進位科學運算質尾數為純小數而非混合小數,等同於變相增加了指數範圍,而這一範圍是正規浮點數指數無法到達的。
在次正規浮點數對應的二進位科學運算質中,出現在尾數小數點後以及尾數第一個1
之前的0
越多,指數變相增加的範圍就越大。為了方便說明,我們以十進位科學運算質0.00003×10⁻³⁸
為例,他等同於3×10⁻⁴³
,指數變相由-38
成為了-43
。
IEEE 754 二進位浮點數特殊值的表示
當指數編碼值和尾數儲存的二進位內容均等價於0
時,浮點數表示數值0
。
當指數編碼值對應的十進位數值等於2ⁿ⁻¹-1
時(其中n
表示指數在儲存空間中占用的 bit 個數),則將根據尾數決定浮點數表示的特殊值,尾數儲存的二進位內容等價於0
表示無限大,不等價於0
表示非數值NaN
。
程式設計語言中的二進位浮點數型別
幾乎所有的程式設計語言都實作了 IEEE 754 標準的二進位浮點數,比如,C# 中的float
和double
型別,Python 中的float
類別。這樣做的好處是可以利用同樣采用 IEEE 754 二進位格式標準的浮點數硬體,隨著浮點數硬體效能的升階,程式執行的效率會得到改善。
運算規則不適用於浮點數型別
浮點數使用有限的儲存空間來表示極大範圍的數值,因此數值中的一些數位可能會被忽略。從這個角度講,浮點數是一種近似值,他無法保證某些運算規則的正確性。比如,當x
與y
不相等時,a+x
與a+y
未必不相等,因為x
,y
可能在加法運算後被忽略。
在 C# 中,浮點數0.00000000000000000001f
不等於浮點數0.00000000000000000002f
,而1.0f+0.00000000000000000001f
卻可以等於1.0f+0.00000000000000000002f
,因為小數部分在加法運算後被忽略。
// 不相等,輸出 False
Console.WriteLine(0.00000000000000000001f == 0.00000000000000000002f);
// 相等,輸出 True
Console.WriteLine(1.0f + 0.00000000000000000001f == 1.0f + 0.00000000000000000002f);
False
True
二進位浮點數的近似性與不準確無關
二進位浮點數不準確的根源是進位的轉換,而近似則是其本身的設計理念,如果提供足夠的儲存空間,近似的情況可能會消失。
程式設計語言中的十進位浮點數型別
在程式設計語言中,並非所有的浮點數型別都采用了 IEEE 754 的二進位格式,比如,C/C++ 編譯器 GCC 支援的_Decimal32
,_Decimal64
,_Decimal128
,他們是十進位格式的浮點數型別。十進位浮點數型別沒有二進位浮點數型別不準確的問題,因此可用於精確計算,比如,統計銀行存款。
程式設計語言中十進位浮點數型別的運算效率
雖然十進位浮點數型別沒有了不準確的問題,但其運算效率可能不及二進位浮點數,因為缺少浮點數硬體的有效支援。硬體可能無法直接運算十進位數值,而是需要其自身或軟體進行某種轉換,這降低了運算效率。
當然,如果有需求,不排除未來一些浮點硬體升階十進位浮點數運算能力。