前言
我們都遇到過如下計算結果:
為什麼會出現如此結果?難道不為 0.3 嗎?這涉及到 js 的精度問題。
首先 js 的數字類型採用基於 IEEE 754 標準來實現的(也稱為浮點數)。其選用的精度格式是:雙精度格式(64 位的二進制數)
這篇就稍稍深入瞭解下雙精度浮點數,以及有關於數 Number 的問題。
IEEE 754 標準
IEEE 二進制浮點數算術標準(IEEE 754),是最廣泛使用的浮點數運算標準,為許多 CPU 與浮點運算器所採用。
這個標準定義了表示浮點數的格式(包括負零-0)與反常值(denormal number),一些特殊數值((無窮(Inf)與非數值(NaN)),以及這些數值的“浮點數運算符”;它也指明瞭四種數值舍入規則和五種例外狀況(包括例外發生的時機與處理方式)。
規定了四種表示浮點數值的方式:單精確度(32 位)、雙精確度(64 位)、延伸單精確度(43 比特以上,很少使用)與延伸雙精確度(79 比特以上,通常以 80 位實現)
64 位的雙精度
下圖,基本解釋清楚 64 位數的組成部分:
64位的雙精度(維基百科)
- sign bit(S 符號):符號位,表示正負號(0 為負數,1 為正數)
- exponent(E 指數):表示次方數,在(二進制的)科學計數法中定義 2 的多少次冪
- mantissa(M 尾數):表示精確度(小數部分,規範中會省略個位數上的 1 )
那麼一個雙精度值的表達式如下:
我知道會有很多地方看不懂,沒事,我下面來具體解釋一下。
符號位 sign
符號位很容易理解,表示整個數的正負值。
所以用此位來示意數的正負性。
尾數位 mantissa
尾數也被稱為規約形式的浮點數,因為在科學計數法的顯示下,分數(fraction 也是 mantissa 那個部分之一)部分最高有效為是 1 (個位數)
最終 mantissa 會以 000001 來示意,會被規範成 1.M 格式 ,其中 1 會被隱藏掉,所以最大是表達 53 位的數(如上圖,實際 mantissa 只有 52 位)。
指數位 exponent
科學計數法
我知道各位都是受過義務教育的,不過我真的忘記了,簡單回顧下把:
科學記數法是一種記數的方法。把一個數表示成 a 與 10 的 n 次冪相乘的形式(1≤|a|<10,n 為整數),這種記數法叫做科學記數法。
那和科學計數法有什麼關係,應該注意到 指數位是 2 的 n 次冪 (為何不是 10 ,因為是二進制)。
如果我們要表達 100(2),則結果如下:
指數偏移值(exponent bias)
我們瞭解科學計數法是可以表示大於 1 ,或者小於 1 的數(小數),即:通過正負指數的值來標識顯示。
由於指數位的 11 位不包括符號位,那麼為了達到這樣正負的效果,就引入了 指數的偏移值。
為什麼要引入這個概念?我想了很久,以下這個例子或許會給你啟發:
指數如果是 1023 和 1024,到底哪個值誰大?
首先 11 位的指數位對應的二進制最大和最小結果值為:00000000000(0) ,11111111111(2047,2^(12-1)-1),即指數的取值範圍為:[0,2047]
並且我們知道指數具有
正負值 (來控制小數點左右移位),那麼我們按照二進制中負數的規則(取反,補位),那麼指數值為 [0,1023] 區間內為正數,[1024,2047] 內為負數(二進制中負數最高位為 1 )。另外,根據 IEEE 規範, 0 和 2047 兩個最值需要做特殊用途,所以這裡移除,所以整個規範的指數取值範圍是:
[1,2046]回到這個問題,1023 和 1024 到底誰大,按照上面區間的劃分,明顯是 1023 > 1024 (正數大於負數,但機器不那麼想)。
崩潰!就我個人理解起來就很困難,更不談實際運算了(當你看到一個大於 1023 的值,還需要引入符號位,補碼之類的計算方式)。
所以引入偏移值 bias(bias = 1023),使得整個運算簡單,好理解。
那麼 [-1022+bias,1023+bias] 等同於 [1,2046],這樣拋去了符號位的影響, 最終:1023 就變成了 0 ,1024 變成了 1 ,明顯 1 的指數值更大。
至於為何是 1023 ,我給的建議是 2046/2 (雖然這樣理解是不對的,我認為數學功底真的對編程用處很大,雖然全還給老師了),另外 32 位精度浮點數的指數偏移量是 127 。
標準(規格)和非標準(規格)
整個指數位的值分為三種情況:
解惑些問題
整數範圍
當尾數為標準模式時:1.M ,尾數位供 52 位,加上隱藏位 1 ,整個精度會是 53 位。
那麼整數的取值範圍是 [-2^53-1,2^53-1]
最小精度
在尾數的 52 位中,使得有一個最小的位定義(1.00000~ 中間 51 個 0~00001),即 2^-52 。
0.1+0.2 等於什麼?
按照最小精度,即打滿 52 位,那麼像 0.1 和 0.2 最終無限循環後的:
最後
對於這個 IEEE 754 規範,我理解的還不是很透徹,不過對於 js 精度上的問題也算是有個初步的解答。
如果有不對之處望各位留言指正。
閱讀更多 前端雨爸 的文章