IEEE 754二進制浮點數算術標准


可能很多人都遇到過浮點數精度丟失的問題,下面以JavaScript為例。

1 - 0.9 = 0.09999999999999998

納尼,不應該是0.1么,怎么變成0.09999999999999998呢?這就要從ECMAScript標准講起了。

 

ECMAScript 並不像其他編程語言一樣對數值類型進行比較具體的划分。ECMAScript 中並不區分整數和浮點數,也不區分不同長度的整數和浮點數。

ECMAScript 中的 Number 類型始終使用 64 位雙精度浮點數來表示數值。這一方面使得處理起來變得簡單,另外一方面也限制了可以表示的數值的范圍。ECMAScript 中 Number 類型的值的個數是 264-253+3。個數后面的“+3”表示的是 Number 類型的 3 個特殊值,分別是 NaN、+Infinity 和 -Infinity。

 

NaN 的含義是“不是一個數字(Not-A-Number)”。在代碼中可以直接通過“NaN”的方式來引用這個值。代碼中與數值相關的計算的結果也可能是 NaN。一般來說,對於 ECMAScript 語言中的操作符,如果其中一個操作數為 NaN,那么計算結果為 NaN。當需要判斷一個變量引用 a 是否為 NaN 時,只需要判斷 a !== a 是否為 true 即可。+Infinity 和 -Infinity 分別表示正無窮大和負無窮大,可以在代碼中直接引用,也可能是某些數值運算的結果。如運算“3 / 0”的結果是 Infinity。除了這 3 個特殊值之外,剩下的數值中一半是正數,一半是負數。數值 0 也有正數和負數兩種形式,稱為正 0 和負 0,分別用 +0 和 -0 來表示。

 

講完了JavaScript的實現標准,現在來了解一下目前最通用的IEEE二進制浮點數算術標准(IEEE Standard for Binary Floating-Point Arithmetic,簡稱 IEEE 754 標准)。

 

IEEE 754 標准的主要起草者是加州大學伯克利分校數學系的 William Kahan 教 授,他幫助 Intel 公司設計了 8087 浮點處理器,並以此為基礎形成了 IEEE 754 標 准, Kahan 教授也因此獲得了 1987 年的圖靈獎。Kahan教授的個人主頁:https://people.eecs.berkeley.edu/~wkahan/

 

IEEE 754標准中關於浮點數的四種格式:

  • 兩種基本的浮點數:單精確度 (32 位字長) 和雙精確度 (64 位字長)。其中單精度格式具有 24 位有效數字,而雙精度格式具有 53 位有效數字,相對於十進制來說,分別是 7 位 (224 ≈ 107) 和 16 位 (253 ≈ 1016) 有效數字。
  • 兩種擴展的浮點數:單精度擴展雙精度擴展。此標准並未規定擴展格式的精度和大小,但它指定了最小精度和大小:單精度擴展需 43 位字長以上,雙精確度擴展需 79 位字長以上 (64 位有效數字)。單精度擴展很 少使用,而對於雙精確度擴展,不同的機器構架中有不同的規定,有的為80 位字長 (X86),有的為 128 位字長 (SPARC)。

 

這里我們只簡單介紹單、雙精度,其中重點介紹單精度,雙精度與單精度原理是一樣的,只是表示的位數長度不同。

 

浮點數的組成(sign 符號、exponent 指數、fraction 尾數):

IEEE標准采用類似於科學計數法的方式表示浮點小數,即我們將每一個浮點數表示為 V = (-1)s * M * 2E

1)(-1)s表示符號位,當s=0,V為正數;當s=1,V為負數。
2)M 表示有效數字,1 ≤ M < 2。
3)2E 表示指數位。

 

從公式  V = (-1)s * M * 2E  我們可以得出:

1) 符號位:確定正、負。

2) 尾數的位數:確定精度。

3) 指數的位數:確定所能表示的數的范圍。

 

所謂科學計數法,我舉一個例子(左移/右移指數的多少位,我們知道在二進制中左移一位表示乘以2,右移一位表示除以2,當移動N位時就是2N,N可為正也可為負)。

18800000000 = 1.88 x  1010

0.000000000188 = 1.88 x 10 -10

 

64位的指數位長度為11,32位的指數長度位為8,所以64位雙精度所能表示的范圍遠大於32位。

 

二進制浮點數是以符號數值表示法的格式存儲 —— 最高有效位被指定為符號位(sign bit);“指數部分”,即次高有效的e個比特,存儲指數部分;最后剩下的f個低有效位的比特,存儲“有效數”(significand)的小數部分(在非規約形式下整數部分默認為0,其他情況下一律默認為1)。

 

指數偏移值(exponent bias),是指浮點數表示法中的指數域的編碼值為指數的實際值加上某個固定的值,IEEE 754標准規定該固定值為 2e-1,其中的 e 為存儲指數的比特的長度。單精度為8,雙精度為11。所以單精度的固定偏移值是28-1 – 1 = 128 – 1 = 127,而雙精度的固定偏移值是211-1 – 1 = 1024 – 1 = 1024。

 

指數實際的存儲:指數的值可能為負數,如果采用補碼表示的話,全體符號位S和Exp自身的符號位將導致不能簡單的進行大小比較。正因為如此,指數部分通常采用一個無符號的正數值存儲。單精度的指數部分是-126 ~ +127,加上固定偏移值127,指數值的大小從1 ~ 254(0和255是特殊值)。浮點小數計算時,指數值減去固定偏移值將是實際的指數大小。

 

采用指數的實際值加上固定的偏移值的辦法表示浮點數的指數,好處是可以用長度為 e 個比特的無符號整數來表示所有的指數取值,這使得兩個浮點數的指數大小的比較更為容易,實際上可以按照字典序比較兩個浮點表示的大小。這種移碼表示的指數部分,中文稱作階碼

 

針對階碼E的值,浮點數的值可以分為三種不同的類型:規格化數(正規數)、非規格化數(次正規數)、特殊值。

單精度格式位模式

0 < e < 255

(−1)s × 1. f × 2e−127  (正規數)

e = 0, f ≠ 0

(−1)s × 0. f × 2−126  (次正規數)

e = 0, f = 0

(−1)s × 0.0 (有符號的零)

s = 0, e = 255, f = 0

+Infinity (正元窮大,特殊值)

s = 1, e = 255, f = 0

+Infinity (負元窮大,特殊值)

e = 255, f ≠ 0

NaN (非數、非確定值,特殊值)

 

 

 

這么講,還是有點暈,我們通過二個數字的示例來詳細說明。

 

第一個示例:263.3,先拆解一下:263.3 => 整數 263 + 小數0.3

 

正式開始推算前,先介紹三種十進制轉二進制的三種方法:除基取余法、減權定位法、乘基取整法。

三種方法來自中科大《計算機組成原理 | 第6章 計算機的運算方法》的相關資料,這里只介紹除基取余法,因為我們接下來計算的就是用的這個方法。

 

除基取余法:把給定的數除以基數,取余數作為最低位的系數,然后繼續將商部分除以基數,余數作為次低位系數,重復操作,直至商為0。

 

以下推導過程我在紙上寫出來了。

 

計算結果與官網的進行對比如下。IEEE 754 Floating Point Converter>>

最終的二進制表示:"0100,0011,1000,0011,1010,0110,0110,0110" 

 

同理45.45也可以這樣進行計算。(45)10 = (101101)2             

45.45 –> 101101.0111001100…(1100循環)

可以使用toString查看十進制轉二進制的結果,與上面計算出來的結果進行對比。

Number(45.45).toString(2)
"101101.0111001100110011001100110011001100110011001101"

 

套公式:V = (-1)s * M * 2E

S = 0  (0)

E = 5  (127 + 5 = 132 –> 10000100)

M = 011010111001100… (1100循環)

 

最終的二進制表示:"0100,0010,0011,0101,1100,1100,1100,1101"

注意最后一位,變成了1101,進行了舍入操作,上面的263.3也進行了舍入操作。

 

舍入的規則是怎么定義的呢,我查了很多資料,暫時還沒有弄的特別清楚 。我搜索到這樣一份PPT,供大家參考,其它資料其實與這個說法類似,清華大學的《浮點數誤差與誤差復雜度》

因為表示方法限制了浮點數的范圍和精度,浮點運算只能近似地表示實數運算。IEEE 浮點數格式定義了四種不同的的舍入方式:

1) 向偶數舍入(默認,不是四舍五入)

2) 向零舍入 (取整)

3) 向上舍入 (ceil)

4) 向下舍入 (floor)

 

向0(截斷)舍入:C/C++的類型轉換。(int) 1.324 = 1,(int) -1.324 = -1;

向負無窮大(向下)舍入:C/C++函數floor()。例如:floor(1.324) = 1,floor(-1.324) = -2。

向正無窮大(向上)舍入:C/C++函數ceil()。ceil(1.324) = 2。Ceil(-1.324) = -1;

 

正是因為舍入的存在,誤差的存就就成了必然,精確只是偶然的。做數據算法,惟一能做的就是誤差不積累。

 

關於浮點數,還有一些知識點是沒有講的,例如浮點異常:無效運算、被零除、上溢、下溢和不精確,以及相關的一些運算示例。

 

 

 

參考資料:

深入探討 ECMAScript 規范第五版

IEEE-754 Floating Point Converter

Decimal to IEEE 754 Floating Point Representation

清華大學的《浮點數誤差與誤差復雜度》

南京大學的《數據的機器信表示》

中國科技大學《計算機組成原理 | 第6章 計算機的運算方法》

華東師范大學《IEEE浮點運算標准》

計算機組成原理課件/CH02-5浮點數(2.9).pdf


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM