遇到的問題:
項目中出現了 17652.19 + 7673.78 - 25325.97 = -3.64 的問題,最后發現是JS精度丟失的問題,那么就先來看看這個結果是怎么產生的。
產生原因:
JavaScript 中所有數字包括整數和小數都只有一種類型 — Number。它的實現遵循 IEEE 754 標准,使用 64 位固定長度來表示,也就是標准的 double 雙精度浮點數(相關的還有float 32位單精度)。為什么呢,因為這樣節省存儲空間。
也就是說 17652.19 + 7673.78 = 25325.969999999998。其實最簡單的例子是 0.1+0.2 = 0.30000000000000004
0.1的二進制表示的是一個無限循環小數,該版本的 JS 采用的是浮點數標准需要對這種無限循環的二進制進行截取,從而導致了精度丟失,造成了0.1不再是0.1,截取之后0.1變成了 0.100…001,0.2變成了0.200…002。所以兩者相加的數大於0.3。
將0.1轉換成為二進制加上0.2的二進制會是53位,但是二進制的最大位數是52位取近似值。
那么還有 0.1+0.2 -0.3 = 5.6是什么問題呢?
因為在輸入內容進行轉換的時候,二進制轉換成十進制,然后十進制轉換成字符串,在這個轉換的過程中發生了取近似值,所以打印出來的是一個近似值。
實際上取到的值是
0.1+0.2-0.3 = 5.551115123125783e-17
在頁面上取兩位小數的時候,會直接從字符串中讀取,會出現值變成 5.56
解決
toFixed()
因為toFixed() 進行並轉換之后是string類型的,需要在進行強制Number() 轉換
Number((0.1+0.2).toFixed(2))
實際是有很多的問題的,存在兼容性問題,在chrome瀏覽器上出現了
335.toFixed(2) = '1.33'
所以需要處理一下兼容性問題,在網上找了一下,有這樣的處理辦法:
通過判斷最后一位是否大於等於5來決定需不需要進位,如果需要進位先把小數乘以倍數變為整數,加1之后,再除以倍數變為小數,這樣就不用一位一位的進行判斷。
// toFixed兼容方法 Number.prototype.toFixed = function(len){ if(len>20 || len<0){ throw new RangeError('toFixed() digits argument must be between 0 and 20'); } // .123轉為0.123 var number = Number(this); if (isNaN(number) || number >= Math.pow(10, 21)) { return number.toString(); } if (typeof (len) == 'undefined' || len == 0) { return (Math.round(number)).toString(); } var result = number.toString(), numberArr = result.split('.'); if(numberArr.length<2){ //整數的情況 return padNum(result); } var intNum = numberArr[0], //整數部分 deciNum = numberArr[1],//小數部分 lastNum = deciNum.substr(len, 1);//最后一個數字 if(deciNum.length == len){ //需要截取的長度等於當前長度 return result; } if(deciNum.length < len){ //需要截取的長度大於當前長度 1.3.toFixed(2) return padNum(result) } //需要截取的長度小於當前長度,需要判斷最后一位數字 result = intNum + '.' + deciNum.substr(0, len); if(parseInt(lastNum, 10)>=5){ //最后一位數字大於5,要進位 var times = Math.pow(10, len); //需要放大的倍數 var changedInt = Number(result.replace('.',''));//截取后轉為整數 changedInt++;//整數進位 changedInt /= times;//整數轉為小數,注:有可能還是整數 result = padNum(changedInt+''); } return result; //對數字末尾加0 function padNum(num){ var dotPos = num.indexOf('.'); if(dotPos === -1){ //整數的情況 num += '.'; for(var i = 0;i<len;i++){ num += '0'; } return num; } else { //小數的情況 var need = len - (num.length - dotPos - 1); for(var j = 0;j<need;j++){ num += '0'; } return num; } } }
一些類庫
math.js,
decimal.js,
D.js
ES6在Number對象上新增了一個極小的常量——Number.EPSILON
Number.EPSILON // 2.220446049250313e-16 Number.EPSILON.toFixed(20) // "0.00000000000000022204"
原文:https://blog.csdn.net/zr15829039341/article/details/102532033