js中如何取精度
一、總結
一句話總結:其實round()函數去經度會有誤差,直接用num.toFixed(2)簡單方便。
toFixed()方法會按照指定的小數返回數值的字符串表示。
var num = 10;
alert(num.toFixed(2));//10.00
1、Math.round(n * Math.pow(10, m)) / Math.pow(10, m) ;
這種方法求精度的問題是什么?
因為后面涉及到了除法,所以會出現19.230000000001的情況,而且也不能保證總是m位的小數,還是用num.toFixed(2)好
2、js精度丟失的原因是什么?
計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 規范,采用雙精度存儲(double precision),占用 64 bit。如圖
意義
- 1位用來表示符號位
- 11位用來表示指數
- 52位表示尾數
浮點數,比如
此時只能模仿十進制進行四舍五入了,但是二進制只有 0 和 1 兩個,於是變為 0 舍 1 入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。
大整數的精度丟失和浮點數本質上是一樣的,尾數位最大是 52 位,因此 JS 中能精准表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。
3、js精度丟失的解決方案是什么?
對於整數,前端出現問題的幾率可能比較低,畢竟很少有業務需要需要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。
對於小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)
二、關於JavaScript中.round()函數的運用
.round()函數是Math(算數)對象中的一種算數任務,主要用於解決數學中四舍五入的問題。
用法
Math.round(number)
number為數字類型,該函數執行后會返回number經過四舍五入處理后的返回值。
例如:
document.write(Math.round(3.64) )
document.write(Math.round(0.46) )
document.write(Math.round(-4.64) )
運行結果
4
0
-5
分析:
因為該round函數通過判斷number的小數點后的部分且以0.5為界限。
number為正數時。
如果 number 的小數部分大於等於 0.5,返回值是大於 number 的最小整數(即number的整數部分加一)。否則,round 返回小於等於 number 的最大整數(即number的整數部分)。
同理,當number為負數時
可以看做先忽略負號來處里,最后在結果返回值時補充回來。
如上面的-4.64.可以先忽略負號,經過round()計算完后得5,再在結果返回時補回來即-5
round()拓展
**【通過上述例子我們不難發現round()函數不能對小數點后面的數字進行精確的保留。】
**倘若我們需要對number后的小數點進行有效的保留,我們該怎么做。
例如將4.6545進行四舍五入,且保留小數點后面的三位小數
由於Math.round()函數並沒有提供保留小數點的功能,所以需要通過創建函數來進行改造
function Newround(n, m){//此處的n表示要四舍五入的數,m為要保留的小數位數
Math.round(n * Math.pow(10, m)) / Math.pow(10, m) ;
}
//此處先將n乘以10的m次方,將要保留的小數位數的小數部分轉成整數部分,然后再用.round()進行四舍五入處理。最后再除以10的m次方還原小數部分
Newround(4.6545, 3); // 得到 4.655
//Math.pow()函數為冪函數
//用法
Math.pow(n,m)//返回的是n的m次方的值
//例子:
Math.pow(5,3)//返回值5*5*5即125
以上是博主對JavaScript中的.round()函數的理解,如有其它經驗,請各位大神補充,謝謝
參考:關於JavaScript中.round()函數的運用 - CSDN博客
https://blog.csdn.net/weixin_36059749/article/details/52459066
三、js精度問題
現象
原因
計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 規范,采用雙精度存儲(double precision),占用 64 bit。如圖
意義
- 1位用來表示符號位
- 11位用來表示指數
- 52位表示尾數
浮點數,比如
此時只能模仿十進制進行四舍五入了,但是二進制只有 0 和 1 兩個,於是變為 0 舍 1 入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。
大整數的精度丟失和浮點數本質上是一樣的,尾數位最大是 52 位,因此 JS 中能精准表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。
大於 9007199254740992 的可能會丟失精度
9007199254740992 >> 10000000000000...000
// 共計 53 個 0
9007199254740992 + 1 >> 10000000000000...001
// 中間 52 個 0
9007199254740992 + 2 >> 10000000000000...010
// 中間 51 個 0
實際上
9007199254740992 + 1
// 丟失
9007199254740992 + 2
// 未丟失
9007199254740992 + 3
// 丟失
9007199254740992 + 4
// 未丟失
以上,可以知道看似有窮的數字, 在計算機的二進制表示里卻是無窮的,由於存儲位數限制因此存在“舍去”,精度丟失就發生了。
想了解更深入的分析可以看這篇論文(又長又臭):What Every Computer Scientist Should Know About Floating-Point Arithmetic
解決方案
對於整數,前端出現問題的幾率可能比較低,畢竟很少有業務需要需要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。
對於小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)
// 0.1 + 0.2
(0.1*10 + 0.2*10) / 10 == 0.3
// true
以下是我寫了一個對象,對小數的加減乘除運算丟失精度做了屏蔽。當然轉換后的整數依然不能超過 9007199254740992。
/** * floatTool 包含加減乘除四個方法,能確保浮點數運算不丟失精度 * * 我們知道計算機編程語言里浮點數計算會存在精度丟失問題(或稱舍入誤差),其根本原因是二進制和實現位數限制有些數無法有限表示 * 以下是十進制小數對應的二進制表示 * 0.1 >> 0.0001 1001 1001 1001…(1001無限循環) * 0.2 >> 0.0011 0011 0011 0011…(0011無限循環) * 計算機里每種數據類型的存儲是一個有限寬度,比如 JavaScript 使用 64 位存儲數字類型,因此超出的會舍去。舍去的部分就是精度丟失的部分。 * * ** method ** * add / subtract / multiply /divide * * ** explame ** * 0.1 + 0.2 == 0.30000000000000004 (多了 0.00000000000004) * 0.2 + 0.4 == 0.6000000000000001 (多了 0.0000000000001) * 19.9 * 100 == 1989.9999999999998 (少了 0.0000000000002) * * floatObj.add(0.1, 0.2) >> 0.3 * floatObj.multiply(19.9, 100) >> 1990 * */ var floatTool = function() { /* * 判斷obj是否為一個整數 */ function isInteger(obj) { return Math.floor(obj) === obj } /* * 將一個浮點數轉成整數,返回整數和倍數。如 3.14 >> 314,倍數是 100 * @param floatNum {number} 小數 * @return {object} * {times:100, num: 314} */ function toInteger(floatNum) { var ret = {times: 1, num: 0} if (isInteger(floatNum)) { ret.num = floatNum return ret } var strfi = floatNum + '' var dotPos = strfi.indexOf('.') var len = strfi.substr(dotPos+1).length var times = Math.pow(10, len) var intNum = parseInt(floatNum * times + 0.5, 10) ret.times = times ret.num = intNum return ret } /* * 核心方法,實現加減乘除運算,確保不丟失精度 * 思路:把小數放大為整數(乘),進行算術運算,再縮小為小數(除) * * @param a {number} 運算數1 * @param b {number} 運算數2 * @param digits {number} 精度,保留的小數點數,比如 2, 即保留為兩位小數 * @param op {string} 運算類型,有加減乘除(add/subtract/multiply/divide) * */ function operation(a, b, op) { var o1 = toInteger(a) var o2 = toInteger(b) var n1 = o1.num var n2 = o2.num var t1 = o1.times var t2 = o2.times var max = t1 > t2 ? t1 : t2 var result = null switch (op) { case 'add': if (t1 === t2) { // 兩個小數位數相同 result = n1 + n2 } else if (t1 > t2) { // o1 小數位 大於 o2 result = n1 + n2 * (t1 / t2) } else { // o1 小數位 小於 o2 result = n1 * (t2 / t1) + n2 } return result / max case 'subtract': if (t1 === t2) { result = n1 - n2 } else if (t1 > t2) { result = n1 - n2 * (t1 / t2) } else { result = n1 * (t2 / t1) - n2 } return result / max case 'multiply': result = (n1 * n2) / (t1 * t2) return result case 'divide': return result = function() { var r1 = n1 / n2 var r2 = t2 / t1 return operation(r1, r2, 'multiply') }() } } // 加減乘除的四個接口 function add(a, b) { return operation(a, b, 'add') } function subtract(a, b) { return operation(a, b, 'subtract') } function multiply(a, b) { return operation(a, b, 'multiply') } function divide(a, b) { return operation(a, b, 'divide') } // exports return { add: add, subtract: subtract, multiply: multiply, divide: divide } }();
// toFixed 修復 function toFixed(num, s) { var times = Math.pow(10, s) var des = num * times + 0.5 des = parseInt(des, 10) / times return des + '' }
相關:
http://0.30000000000000004.com
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
https://www.cnblogs.com/panpanwelcome/p/7109718.html