前言
經常使用JavaScript用來處理數字的程式設計師都知道,JavaScript的Number.toFixed
,這一函式,在格式化數字時,會自動進行四捨五入,例如:
10.125.toFixed(2) ---> "10.13"
但是在某些情況下,其四捨五入的結果,往往都不盡人意,例如:
10.145.toFixed(2) ---> "10.14"
那為何為出現上述這種情況,需要從進位制談起。
進位制之謎
眾所周知,計算機在設計之初,出於各方面角度考慮,最終採用二進位制的格式來儲存資料。而我們平時對於數字所慣用的進位制是十進位制。用二進位制的格式來儲存十進位制的資料,必然會面臨進位制的轉換,進位制的轉換就會面臨精度的丟失。
例如,對於⅓,對於三進位制的數來說是0.1,而對於十進位制來說,是0.333(3迴圈)。那對於三個⅓相加,對於三進位制來說,就是一(逢三進一),而對於十進位制來說,就是0.999(9迴圈)。
同樣的情況,也會出現在十進位制和二進位制的轉換中。當我們在計算機中,宣告一個變數為10.145,其實該數字作為二進位制儲存在計算機中,並不真的是10.145。可以通過Number.prototype.toPrecision
方法來一探究竟。
Number.prototype.toPrecision
方法以指定的精度返回該數值物件的字串表示。
10.145.toPrecision(21) ---> "10.1449999999999995737"
因此就可以解釋,為什麼toFixed()
方法返回的四捨五入的值,在某些情況不符合預料。
同樣,也告訴大家,浮點數的所有計算,加減乘除無一例外,在某些情況下,都會出現不符合預料的情況,最常見的如0.1+0.2
,等等。
解決方案
針對上述情況,我寫了一個簡單的庫,可以滿足數字的四捨五入需求。round-js
探索浮點數標準
和大部分語言不同,JavaScript目前對於數字的表示,只有一種型別,即Number,採用64位雙精度浮點數來表示一個數,其標準與大多數語言一樣,採用IEEE-754標準。IEEE-754下64位雙精度浮點數的標準,可以用一張圖來表示,如下:
sign:代表符號,用1位來表示,即正數負數,1代表正數,0代表負數;
exponent:代表指數,底數為2,用11位來表述;
fraction:真正的有效數字,用52位來表示。
最終,任何一個數,對於該標準,都使用如下公式來表示得出:
其中:
V:結果
S:上面的sign
M:有效數字fraction
E:上面的exponent
可以看出,這個表示方法,類似十進位制中的科學計數法。
對於這些概念,IEEE-754標準中還有許多規定,例如<1M<2
,等等,更多細節就不展開了。
完。