1. 程式人生 > >JavaScript中科學計數法轉化為數值字串形式

JavaScript中科學計數法轉化為數值字串形式

原文地址:https://www.css88.com/archives/9318 (受益匪淺)

JavaScript 中經常會碰到數值計算問題,偶爾會在不經意間報一個不是bug的bug。今天來說說一個特殊的例子。我以0.0011BTC 價格買入 0.0002CZR 計算出了的金額是 0.00000022BTC,而 JavaScript 計算出來的金額是 2.2e-7 。值是對的,只是用了科學計數法,也是數值型別。但是問題來了,一般使用者使用者看不懂 2.2e-7,那麼就把它轉換成 0.00000022 吧。然而問題了,我用盡辦法,怎麼樣都無法將 2.2e-7 轉換成直觀的 0.00000022

。或許你會嘲笑我,告訴我直接用 .toFixed() 方法。但是新問題又來了, .toFixed() 會保留足夠的小數位,比如:2e-7.toFixed(8) 得到的值是 0.000000202e2.toFixed(8)得到的值是 200.00000000。最後的 0 讓我感到多餘…

問題分析

問題還是要解決,只能深入瞭解 JavaScript 中科學計數法相關的知識。對於極大或者極小的數,可以用科學計數法 e來表示的浮點數值來表示。科學計數法允許字母e 或 E 的後面,跟著一個整數,表示這個數值的指數部分。

以下兩種情況,JavaScript 會自動將數值轉為科學計數法表示

(1) 小於1且小數點後面帶有6個0以上的浮點數值:

JavaScript 程式碼:
  1. 0.0000003 // 3e-7
  2. 0.00000033 // 3.3e-7
  3. 0.000003 // 0.000003

(2) 整數位數字多於21位:

JavaScript 程式碼:
  1. 1234567890123456789012 //1.2345678901234568e+21
  2. 1234567890123456789012.1 //1.2345678901234568e+21
  3. 123456789012345678901 //123456789012345680000

解決思路

首先看看整數位數字多於21位的情況,其實這個一般不會碰到,整數位數字多於21位已經超出了 JavaScript 精確整數範圍 −9007199254740992 至 9007199254740992 (即正負2的53次方)。如果你需要可以是使用 

bignumber.js。一般情況你可以使用.toString() 將科學計數法的數字轉化為直觀的數字表示,例如:

JavaScript 程式碼:
  1. ""+1.401e10 // "14010000000"
  2. 1.401e10.toString(10) // "14010000000"

小於1且小數點後面帶有6個0以上的浮點數值自動轉化為科學計數法,要想轉換成直觀的數字表示就沒那麼容易了,我嘗試了幾種辦法:

JavaScript 程式碼:
  1. ""+3.3e-7 //"3.3e-7"
  2. 3.3e-7.toString(10) //"3.3e-7"

都沒達到我的預期。

解決問題

精度計算的時候我們通常會使用 .toFixed() 方法,Number.toFixed(digits) 方法使用定點表示法來格式化一個數,會對結果進行四捨五入。引數 digits 表示小數點後數字的個數,一般介於 0 到 20 (包括)之間。例如:

JavaScript 程式碼:
  1. 3.3e-7.toFixed(8); // "0.00000033"
  2. 3e-7.toFixed(8); // "0.00000030"

一般情況下,我們的需求小數位數是固定的,所以這個基本可以滿足我們的需求。但是有些人可能不喜歡 0.00000030 這種形式,認為最後的 0 是多餘的。所以索性就改進了一下:

JavaScript 程式碼:
  1. function toNumberStr(num,digits) {
  2. // 正則匹配小數科學記數法
  3. if (/^(\d+(?:\.\d+)?)(e)([\-]?\d+)$/.test(num)) {
  4. // 正則匹配小數點最末尾的0
  5. var temp=/^(\d{1,}(?:,\d{3})*\.(?:0*[1-9]+)?)(0*)?$/.exec(num.toFixed(digits)) ;
  6. if(temp){
  7. return temp[1];
  8. }else{
  9. return num.toFixed(digits)
  10. }
  11. }else{
  12. return ""+num
  13. }
  14. }
  15. toNumberStr(3.3e-7,8) // "0.00000033"
  16. toNumberStr(3e-7,8) // "0.0000003"
  17. toNumberStr(1.401e10,8) // "14010000000"
  18. toNumberStr(0.0004,8) // "0.0004"

這個方法基本滿足了我的需求,但是總是覺得一點累贅,後面那個引數意思也不夠明確,所以發到微信群請大家幫忙優化。特別感謝網友 @caikan 提供的方法:

JavaScript 程式碼:
  1. function toNonExponential(num) {
  2. var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
  3. return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
  4. }
  5. toNonExponential(3.3e-7) // "0.00000033"
  6. toNonExponential(3e-7) // "0.0000003"
  7. toNonExponential(1.401e10) // "14010000000"
  8. toNonExponential(0.0004) // "0.0004"

解析一下:

.toExponential()將數字轉化為科學記數法表示,匹配正則表示式/\d(?:\.(\d*))?e([+-]\d+)/,獲取科學記數法中小數點後的字元及冪指數(e 後面的值),這樣可以確定數字是幾位小數。再用toFixed() 轉換成數值表示。