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.00000020
,2e2.toFixed(8)
得到的值是 200.00000000
。最後的 0
讓我感到多餘…
問題分析
問題還是要解決,只能深入瞭解 JavaScript 中科學計數法相關的知識。對於極大或者極小的數,可以用科學計數法 e
來表示的浮點數值來表示。科學計數法允許字母e
或 E
的後面,跟著一個整數,表示這個數值的指數部分。
以下兩種情況,JavaScript 會自動將數值轉為科學計數法表示
(1) 小於1且小數點後面帶有6個0以上的浮點數值:
JavaScript 程式碼:- 0.0000003 // 3e-7
- 0.00000033 // 3.3e-7
- 0.000003 // 0.000003
(2) 整數位數字多於21位:
JavaScript 程式碼:- 1234567890123456789012 //1.2345678901234568e+21
- 1234567890123456789012.1 //1.2345678901234568e+21
- 123456789012345678901 //123456789012345680000
解決思路
首先看看整數位數字多於21位的情況,其實這個一般不會碰到,整數位數字多於21位已經超出了 JavaScript 精確整數範圍 −9007199254740992 至 9007199254740992 (即正負2的53次方)。如果你需要可以是使用 .toString()
將科學計數法的數字轉化為直觀的數字表示,例如:
- ""+1.401e10 // "14010000000"
- 1.401e10.toString(10) // "14010000000"
小於1且小數點後面帶有6個0以上的浮點數值自動轉化為科學計數法,要想轉換成直觀的數字表示就沒那麼容易了,我嘗試了幾種辦法:
JavaScript 程式碼:- ""+3.3e-7 //"3.3e-7"
- 3.3e-7.toString(10) //"3.3e-7"
都沒達到我的預期。
解決問題
精度計算的時候我們通常會使用 .toFixed()
方法,Number.toFixed(digits)
方法使用定點表示法來格式化一個數,會對結果進行四捨五入。引數 digits
表示小數點後數字的個數,一般介於 0 到 20 (包括)之間。例如:
- 3.3e-7.toFixed(8); // "0.00000033"
- 3e-7.toFixed(8); // "0.00000030"
一般情況下,我們的需求小數位數是固定的,所以這個基本可以滿足我們的需求。但是有些人可能不喜歡 0.00000030
這種形式,認為最後的 0
是多餘的。所以索性就改進了一下:
- function toNumberStr(num,digits) {
- // 正則匹配小數科學記數法
- if (/^(\d+(?:\.\d+)?)(e)([\-]?\d+)$/.test(num)) {
- // 正則匹配小數點最末尾的0
- var temp=/^(\d{1,}(?:,\d{3})*\.(?:0*[1-9]+)?)(0*)?$/.exec(num.toFixed(digits)) ;
- if(temp){
- return temp[1];
- }else{
- return num.toFixed(digits)
- }
- }else{
- return ""+num
- }
- }
- toNumberStr(3.3e-7,8) // "0.00000033"
- toNumberStr(3e-7,8) // "0.0000003"
- toNumberStr(1.401e10,8) // "14010000000"
- toNumberStr(0.0004,8) // "0.0004"
這個方法基本滿足了我的需求,但是總是覺得一點累贅,後面那個引數意思也不夠明確,所以發到微信群請大家幫忙優化。特別感謝網友 @caikan 提供的方法:
JavaScript 程式碼:- function toNonExponential(num) {
- var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
- return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
- }
- toNonExponential(3.3e-7) // "0.00000033"
- toNonExponential(3e-7) // "0.0000003"
- toNonExponential(1.401e10) // "14010000000"
- toNonExponential(0.0004) // "0.0004"
解析一下:
用.toExponential()
將數字轉化為科學記數法表示,匹配正則表示式/\d(?:\.(\d*))?e([+-]\d+)/
,獲取科學記數法中小數點後的字元及冪指數(e
後面的值),這樣可以確定數字是幾位小數。再用toFixed()
轉換成數值表示。