JavaScript中科学计数法的问题
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次方)。如果你需要可以是使用
bignumber.js
。一般情况你可以使用
.toString()
将科学计数法的数字转化为直观的数字表示,例如:
JavaScript 代码:
""+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 (包括)之间。例如:
JavaScript 代码:
3.3e-7.toFixed(8); // "0.00000033"
3e-7.toFixed(8); // "0.00000030"
一般情况下,我们的需求小数位数是固定的,所以这个基本可以满足我们的需求。但是有些人可能不喜欢
0.00000030
这种形式,认为最后的
0
是多余的。所以索性就改进了一下:
JavaScript 代码:
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+)/);