JavaScript四捨五入的那些坑

weixin_34127717發表於2017-11-09

前言

經常使用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位雙精度浮點數的標準,可以用一張圖來表示,如下:

float.png

  • sign:代表符號,用1位來表示,即正數負數,1代表正數,0代表負數;

  • exponent:代表指數,底數為2,用11位來表述;

  • fraction:真正的有效數字,用52位來表示。

最終,任何一個數,對於該標準,都使用如下公式來表示得出:

其中:

  • V:結果

  • S:上面的sign

  • M:有效數字fraction

  • E:上面的exponent

可以看出,這個表示方法,類似十進位制中的科學計數法。
對於這些概念,IEEE-754標準中還有許多規定,例如<1M<2,等等,更多細節就不展開了。

完。

相關文章