JS Number類型數字位數及IEEE754標准


JS的基礎類型Number,遵循 IEEE 754 規范,采用雙精度存儲(double precision),占用 64 bit。如圖

 

意義

  • 1位用來表示符號位
  • 11位用來表示指數
  • 52位表示尾數

 

浮點數,比如

1
2
0.1 >> 0.0001 1001 1001 1001…(1001無限循環)
0.2 >> 0.0011 0011 0011 0011…(0011無限循環)

此時只能模仿十進制進行四舍五入了,但是二進制只有 0 和 1 兩個,於是變為 0 舍 1 入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。

 

大整數的精度丟失和浮點數本質上是一樣的,尾數位最大是 52 位,因此 JS 中能精准表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。

大於 9007199254740992 的可能會丟失精度

1
2
3
9007199254740992     >> 10000000000000...000  // 共計 53 個 0
9007199254740992 + 1 >> 10000000000000...001  // 中間 52 個 0
9007199254740992 + 2 >> 10000000000000...010  // 中間 51 個 0

 

實際上

1
2
3
4
9007199254740992 + 1  // 丟失
9007199254740992 + 2  // 未丟失
9007199254740992 + 3  // 丟失
9007199254740992 + 4  // 未丟失

 解決方法:

對於整數,前端出現問題的幾率可能比較低,畢竟很少有業務需要需要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。

對於小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)

1
2
// 0.1 + 0.2
(0.1*10 + 0.2*10) / 10 == 0.3  // true

以下摘自百度:

 

IEEE 754標准規定了什么IEEE 754 規定:

a) 兩種基本浮點格式:單精度和雙精度。IEEE單精度格式具有24位有效數字,並總共占用32 位。IEEE雙精度格式具有53位有效數字精度,並總共占用64位。 說明:基本浮點格式是固定格式,相對應的十進制有效數字分別為7位和17位。基本浮點格式對應的C/C++類型為float和double。

b) 兩種擴展浮點格式:單精度擴展和雙精度擴展。此標准並未規定擴展格式的精度和大小,但它指定了最小精度和大小。例如,IEEE 雙精度擴展格式必須至少具有64位有效數字,並總共占用至少79 位。 說明:雖然IEEE 754標准沒有規定具體格式,但是實現者可以選擇符合該規定的格式,一旦實現,則為固定格式。例如:x86 FPU是80位擴展精度,而Intel安騰FPU是82位擴展精度,都符合IEEE 754標准的規定。C/C++對於擴展雙精度的相應類型是long double,但是,Microsoft Visual C++ 6.0版本以上的編譯器都不支持該類型,long double和double一樣,都是64位基本雙精度,只能用其它C/C++編譯器或匯編語言。

c) 浮點運算的准確度要求:加、減、乘、除、平方根、余數、將浮點格式的數舍入為整數值、在不同浮點格式之間轉換、在浮點和整數格式之間轉換以及比較。求余和比較運算必須精確無誤。其他的每種運算必須向其目標提供精確的結果,除非沒有此類結果,或者該結果不滿足目標格式。對於后一種情況,運算必須按照下面介紹的規定舍入模式的規則對精確結果進行最低限度的修改,並將經過此類修改的結果提供給運算的目標。 說明:IEEE754沒有規定基本算術運算(+、-、×、/ 等)的結果必須精確無誤,因為對於IEEE 754的二進制浮點數格式,由於浮點格式長度固定,基本運算的結果幾乎不可能精確無誤。這里用三位精度的十進制加法來說明: 例1:a = 3.51,b = 0.234,求a+b = ? a與b都是三位有效數字,但是,a+b的精確結果為3.744,是四位有效數字,對於該浮點格式只有三位精度,a+b的結果無法精確表示,只能近似表示,具體運算結果取決於舍入模式(見舍入模式的說明)。同理,由於浮點格式固定,對於其他基本運算,結果也幾乎無法精確表示。

d) 在十進制字符串和兩種基本浮點格式之一的二進制浮點數之間進行轉換的准確度、單一性和一致性要求。對於在指定范圍內的操作數,這些轉換必須生成精確的結果(如果可能的話),或者按照規定舍入模式的規則,對此類精確結果進行最低限度的修改。對於不在指定范圍內的操作數,這些轉換生成的結果與精確結果之間的差值不得超過取決於舍入模式的指定誤差。

說明:這一條規定是針對十進制字符串表示的數據與二進制浮點數之間相互轉換的規定,也是一般編程者最容易產生錯覺的事情。因為人最熟悉的是十進制,以為對於任意十進制數,二進制都應該能精確表示,其實不然。本文主要目的就是揭密二進制浮點數所能夠精確表示的十進制數,如果你以前沒有想過這個問題,絕對讓你吃驚。賣個關子先!

e) 五種類型的IEEE 浮點異常,以及用於向用戶指示發生這些類型異常的條件。五種類型的浮點異常是:無效運算、被零除、上溢、下溢和不精確。

說明:關於浮點異常,見Kahan教授的《Lecture Notes on IEEE 754》,這里我就不浪費口水了。

f) 四種舍入方向:向最接近的可表示的值;當有兩個最接近的可表示的值時首選"偶數"值;向負無窮大(向下);向正無窮大(向上)以及向0(截斷)。

說明:舍入模式也是比較容易引起誤解的地方之一。我們最熟悉的是四舍五入模式,但是,IEEE 754標准根本不支持,它的默認模式是最近舍入(Round to Nearest),它與四舍五入只有一點不同,對.5的舍入上,采用取偶數的方式。舉例比較如下: 最近舍入模式:Round(0.5) = 0; Round(1.5) = 2; Round(2.5) = 2; 四舍五入模式:Round(0.5) = 1; Round(1.5) = 2; Round(2.5) = 3; 主要理由:由於字長有限,浮點數能夠精確表示的數是有限的,因而也是離散的。在兩個可以精確表示的相鄰浮點數之間,必定存在無窮多實數是IEEE浮點數所無法精確表示的。如何用浮點數表示這些數,IEEE 754的方法是用距離該實數最近的浮點數來近似表示。但是,對於.5,它到0和1的距離是一樣近,偏向誰都不合適,四舍五入模式取1,雖然銀行在計算利息時,願意多給0.5分錢,但是,它並不合理。例如:如果在求和計算中使用四舍五入,一直算下去,誤差有可能越來越大。機會均等才公平,也就是向上和向下各占一半才合理,在大量計算中,從統計角度來看,高一位分別是偶數和奇數的概率正好是50% : 50%。至於為什么取偶數而不是奇數,大師Knuth有一個例子說明偶數更好,於是一錘定音。最近舍入模式在C/C++中沒有相應的函數,當然,IEEE754以及x86 FPU的默認舍入模式是最近舍入,也就是每次浮點計算結果都采用最近舍入模式,除非用程序顯式設置為其它三種舍入模式。 另外三種舍入模式,簡要說明。 向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; 后兩種舍入方法據說是為了數值計算中的區間算法,但很少聽說哪個商業軟件使用區間算法。


免責聲明!

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



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