之前雖然有看到過 js 精度相關的文章。但也都沒有“印象深刻” ,但是今天"有幸"遇到了。
做一個項目,進行頁面調試的時候,
當數量增加到3時總價格變得好長好長
立馬在控制台驗證了一下,算出這么多個小數。
還好之前有看過這方面的文章,知道是js的精度問題(但也不是js本身的問題,而是二進制的問題)。
正確的應該是 239.7
然后找了幾篇文章,最后打算用那個插件。可以用字符串進行運算,所以功能很強呀。也不大,5k而已~什么?你給我說流量?優化?百度首頁?淘寶?好吧,等有門做那樣的項目時再說吧。
想測試更多精度問題的問題試試:
9007199254740992 + 1 // 丟失 9007199254740992 + 2 // 未丟失 9007199254740992 + 3 // 丟失 9007199254740992 + 4 // 未丟失 var x = 0.3 - 0.2; //30美分減去20美分 var y = 0.2 - 0.1; //20美分減去10美分 x == y; // =>false,兩值不相等 x == 0.1; // =>false,真實值為:0.09999999999999998 y == 0.1; // =>true
使用不同的函數分別計算+、-、*、/ :

//問題比如:7*0.8 JavaScript算出來就是:5.6000000000000005 //加法函數,用來得到精確的加法結果 //說明:javascript的加法結果會有誤差,在兩個浮點數相加的時候會比較明顯。這個函數返回較為精確的加法結果。 //調用:accAdd(arg1,arg2) //返回值:arg1加上arg2的精確結果 function accAdd(arg1, arg2) { var r1, r2, m; try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 } try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 } m = Math.pow(10, Math.max(r1, r2)) return (arg1 * m + arg2 * m) / m } //用法: //給Number類型增加一個add方法,調用起來更加方便。 Number.prototype.add = function (arg) { return accAdd(arg, this); } //如: var t1 = 6.60; var t2 = 1.32; var t3 = 1.2; var t4 = 1.2; var t5 = 1.2; alert(Number(t1).add(Number(t2)).add(Number(t3)).add(Number(t4)).add(Number(t5))); //減法函數,用來得到精確的減法結果 function Subtr(arg1, arg2) { var r1, r2, m, n; try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 } try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 } m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //動態控制精度長度 n = (r1 >= r2) ? r1 : r2; return ((arg1 * m - arg2 * m) / m).toFixed(n); } //乘法函數,用來得到精確的乘法結果 //說明:javascript的乘法結果會有誤差,在兩個浮點數相乘的時候會比較明顯。這個函數返回較為精確的乘法結果。 //調用:accMul(arg1,arg2) //返回值:arg1乘以arg2的精確結果 function accMul(arg1, arg2) { var m = 0, s1 = arg1.toString(), s2 = arg2.toString(); try { m += s1.split(".")[1].length } catch (e) { } try { m += s2.split(".")[1].length } catch (e) { } return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m) } //用法: //給Number類型增加一個mul方法,調用起來更加方便。 Number.prototype.mul = function (arg) { return accMul(arg, this); } //除法函數,用來得到精確的除法結果 //說明:javascript的除法結果會有誤差,在兩個浮點數相除的時候會比較明顯。這個函數返回較為精確的除法結果。 //調用:accDiv(arg1,arg2) //返回值:arg1除以arg2的精確結果 function accDiv(arg1, arg2) { var t1 = 0, t2 = 0, r1, r2; try { t1 = arg1.toString().split(".")[1].length } catch (e) { } try { t2 = arg2.toString().split(".")[1].length } catch (e) { } with (Math) { r1 = Number(arg1.toString().replace(".", "")) r2 = Number(arg2.toString().replace(".", "")) return (r1 / r2) * pow(10, t2 - t1); } } //用法: //給Number類型增加一個div方法,調用起來更加方便。
JS中toFixed()方法的問題及解決方案
四舍五入失效。
《浮點數精度問題的前世今生?為什么計算機會 丟失小數……》
http://www.zhoulujun.cn/zhoulujun/html/theory/computBase/2016_0714_7860.html
1.335.toFixed(2) // 1.33
重寫 toFixed ,有人說重寫后的返回值改變了。
http://www.cnblogs.com/gushen/archive/2012/11/20/2778324.html

<html> <head> <script type="text/javascript"> Number.prototype.toFixed=function (d) { var s=this+""; if(!d)d=0; if(s.indexOf(".")==-1)s+="."; s+=new Array(d+1).join("0"); if(new RegExp("^(-|\\+)?(\\d+(\\.\\d{0,"+(d+1)+"})?)\\d*$").test(s)){ var s="0"+RegExp.$2,pm=RegExp.$1,a=RegExp.$3.length,b=true; if(a==d+2){ a=s.match(/\d/g); if(parseInt(a[a.length-1])>4){ for(var i=a.length-2;i>=0;i--){ a[i]=parseInt(a[i])+1; if(a[i]==10){ a[i]=0; b=i!=1; }else break; } } s=a.join("").replace(new RegExp("(\\d+)(\\d{"+d+"})\\d$"),"$1.$2"); }if(b)s=s.substr(1); return (pm+s).replace(/\.$/,""); }return this+""; }; </script> </head> <body> <input type="button" value="顯示0.009.toFixed(2)" onclick="alert(0.009.toFixed(2))"><br /> <input type="button" value="顯示0.123.toFixed(2)" onclick="alert(0.123.toFixed(2))"><br /> <input type="button" value="顯示0.125.toFixed(2)" onclick="alert(0.125.toFixed(2))"><br /> <input type="button" value="顯示0.126.toFixed(2)" onclick="alert(0.126.toFixed(2))"><br /> <input type="button" value="顯示20.445.toFixed(2)" onclick="alert(20.445.toFixed(2))"><br /> <input onclick="alert(20.405.toFixed(2))" type="button" value="顯示20.405.toFixed(2)"> <br /> <input onclick="alert(20.415.toFixed(2))" type="button" value="顯示20.415.toFixed(2)"> <br /> <input onclick="alert(20.425.toFixed(2))" type="button" value="顯示20.425.toFixed(2)"> <br /> <input onclick="alert(20.435.toFixed(2))" type="button" value="顯示20.435.toFixed(2)"> <br /> <input onclick="alert(20.445.toFixed(2))" type="button" value="顯示20.445.toFixed(2)"> <br /> <input onclick="alert(20.455.toFixed(2))" type="button" value="顯示20.455.toFixed(2)"> <br /> <input onclick="alert(20.465.toFixed(2))" type="button" value="顯示20.465.toFixed(2)"> <br /> <input onclick="alert(20.475.toFixed(2))" type="button" value="顯示20.475.toFixed(2)"> <br /> <input onclick="alert(20.485.toFixed(2))" type="button" value="顯示20.485.toFixed(2)"> <br /> <input onclick="alert(20.495.toFixed(2))" type="button" value="顯示20.495.toFixed(2)"> <br /> <input onclick="alert(0.05.toFixed(1))" type="button" value="顯示0.05.toFixed(1)"> <br /> <input onclick="alert(0.15.toFixed(1))" type="button" value="顯示0.15.toFixed(1)"> <br /> <input onclick="alert(0.25.toFixed(1))" type="button" value="顯示0.25.toFixed(1)"> <br /> <input onclick="alert(0.35.toFixed(1))" type="button" value="顯示0.35.toFixed(1)"> <br /> <input onclick="alert(0.45.toFixed(1))" type="button" value="顯示0.45.toFixed(1)"> <br /> <input onclick="alert(0.55.toFixed(1))" type="button" value="顯示0.55.toFixed(1)"> <br /> <input onclick="alert(0.65.toFixed(1))" type="button" value="顯示0.65.toFixed(1)"> <br /> <input onclick="alert(0.75.toFixed(1))" type="button" value="顯示0.75.toFixed(1)"> <br /> <input onclick="alert(0.85.toFixed(1))" type="button" value="顯示0.85.toFixed(1)"> <br /> <input onclick="alert(0.95.toFixed(1))" type="button" value="顯示0.95.toFixed(1)"> <br /> </body> </html>
下面的文章均來源於網絡。
http://blog.csdn.net/forest_fire/article/details/50944339
如果我問你 0.1 + 0.2 等於幾?你可能會送我一個白眼,0.1 + 0.2 = 0.3 啊,那還用問嗎?連幼兒園的小朋友都會回答這么小兒科的問題了。但是你知道嗎,同樣的問題放在編程語言中,或許就不是想象中那么簡單的事兒了。
不信?我們先來看一段 JS。
var numA = 0.1;
var numB = 0.2;
alert( (numA + numB) === 0.3 );
執行結果是 false。沒錯,當我第一次看到這段代碼時,我也理所當然地以為它是 true,但是執行結果讓我大跌眼鏡,是我的打開方式不對嗎?非也非也。我們再執行以下代碼試試就知道結果為什么是 false 了。
var numA = 0.1;
var numB = 0.2;
alert( numA + numB );
原來,0.1 + 0.2 = 0.30000000000000004。是不是很奇葩?其實對於浮點數的四則運算,幾乎所有的編程語言都會有類似精度誤差的問題,只不過在 C++/C#/Java 這些語言中已經封裝好了方法來避免精度的問題,而 JavaScript 是一門弱類型的語言,從設計思想上就沒有對浮點數有個嚴格的數據類型,所以精度誤差的問題就顯得格外突出。下面就分析下為什么會有這個精度誤差,以及怎樣修復這個誤差。
首先,我們要站在計算機的角度思考 0.1 + 0.2 這個看似小兒科的問題。我們知道,能被計算機讀懂的是二進制,而不是十進制,所以我們先把 0.1 和 0.2 轉換成二進制看看:
0.1 => 0.0001 1001 1001 1001…(無限循環)
0.2 => 0.0011 0011 0011 0011…(無限循環)
雙精度浮點數的小數部分最多支持 52 位,所以兩者相加之后得到這么一串 0.0100110011001100110011001100110011001100110011001100 因浮點數小數位的限制而截斷的二進制數字,這時候,我們再把它轉換為十進制,就成了 0.30000000000000004。
原來如此,那怎么解決這個問題呢?我想要的結果就是 0.1 + 0.2 === 0.3 啊!!!
有種最簡單的解決方案,就是給出明確的精度要求,在返回值的過程中,計算機會自動四舍五入,比如:
var numA = 0.1;
var numB = 0.2;
alert( parseFloat((numA + numB).toFixed(2)) === 0.3 );
但是明顯這不是一勞永逸的方法,如果有一個方法能幫我們解決這些浮點數的精度問題,那該多好。我們來試試下面這個方法:
Math.formatFloat = function(f, digit) {
var m = Math.pow(10, digit);
return parseInt(f * m, 10) / m;
}
var numA = 0.1;
var numB = 0.2;
alert(Math.formatFloat(numA + numB, 1) === 0.3);
這個方法是什么意思呢?為了避免產生精度差異,我們要把需要計算的數字乘以 10 的 n 次冪,換算成計算機能夠精確識別的整數,然后再除以 10 的 n 次冪,大部分編程語言都是這樣處理精度差異的,我們就借用過來處理一下 JS 中的浮點數精度誤差。
如果下次再有人問你 0.1 + 0.2 等於幾,你可要小心回答咯!!
1.因為計算機只認識二進制,所以某些數字二進制是無限循環的,例如:0.1=> 0.0001 1001 1001 ...無限循環 ,所以產生了精度問題,c這類語言已經封裝好方法來避免,然而js並沒有,為此帶來不少的麻煩,特別是需要頻繁計算的項目,出現bug還不容易發現。不扯皮,上解決方案:
1.化零為整
先把小數乘以10的次冪,然后再運算。
0.1+0.2=>((0.1*10)+(0.2*10))/10=>0.3;
當然這只是思路,實際應用還有很多問題,比如要判斷有幾位小數位,當表達式復雜的時候可閱讀性的問題,我的思路是分別寫加減乘除四個運算方法,把四個方法放到windwo對象的原型中(不推薦)或者放到某個模塊類中;
2.CalcEval.js引擎
不想動腦的福利來了,CalcEval引擎專門解決js精度問題。
引入CalcEval.js
<script src="js/CalcEval.js"></script>
var ce=new CalcEval();//創建引擎對象
var result=ce.eval('0.1+0.2');//注意:表達式必須以字符串的形式傳入
我們大家都知道,javascript在計算公式的時候,會出現誤差,導致我們本來就應該正確的代碼,出現了我們意想不到的結果。
例如:
45.6*13=592.8000000000001(結果應該是592.8); 0.7+0.1=0.7999999999999999(應該是0.8); //還有N多,在此不一一列舉。
網上有一個比較認可的解決方法,就是自己去寫加法,減法,乘法,除法。
例如:

// 兩個浮點數求和 function accAdd(num1,num2){ var r1,r2,m; try{ r1 = num1.toString().split('.')[1].length; }catch(e){ r1 = 0; } try{ r2=num2.toString().split(".")[1].length; }catch(e){ r2=0; } m=Math.pow(10,Math.max(r1,r2)); // return (num1*m+num2*m)/m; return Math.round(num1*m+num2*m)/m; } // 兩個浮點數相減 function accSub(num1,num2){ var r1,r2,m; try{ r1 = num1.toString().split('.')[1].length; }catch(e){ r1 = 0; } try{ r2=num2.toString().split(".")[1].length; }catch(e){ r2=0; } m=Math.pow(10,Math.max(r1,r2)); n=(r1>=r2)?r1:r2; return (Math.round(num1*m-num2*m)/m).toFixed(n); } // 兩數相除 function accDiv(num1,num2){ var t1,t2,r1,r2; try{ t1 = num1.toString().split('.')[1].length; }catch(e){ t1 = 0; } try{ t2=num2.toString().split(".")[1].length; }catch(e){ t2=0; } r1=Number(num1.toString().replace(".","")); r2=Number(num2.toString().replace(".","")); return (r1/r2)*Math.pow(10,t2-t1); } function accMul(num1,num2){ var m=0,s1=num1.toString(),s2=num2.toString(); try{m+=s1.split(".")[1].length}catch(e){}; try{m+=s2.split(".")[1].length}catch(e){}; return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m); }
但是有的時候,我們需要計算一連串的公式,並且里面包含了括號等等的復雜的符合運算,這個時候咱們應該怎么辦呢?
例如:計算(0.7+0.1)÷(45.6*13)
這樣的公式,我們是無法通過上面的自定義函數來解決的。因此今天給大家介紹一個比較好的計算引擎。
CalcEval.js
CalcEval引擎是一個專門解決javascript浮點數誤差的的引擎,能夠完美的解決各種復合的運算,最終輸出正確的結果。
使用方法:
第一步:引入CalcEval.js
<script type="text/javascript" src="CalcEval.js"></script>
第二部:在頁面上調用CalcEval的解析引擎入口
var ce = new CalcEval();//創建引擎對象 var result = ce.eval("(0.7+0.1)/(45.6*13)");//調用引擎接口來解析公式的字符串,這個地方,必須要將公式以字符串的形式傳入。 alert(result);//查看返回結果。
就這么簡單的過程,就可以解決了每個瀏覽器中的浮點數計算bug。同時也可以制作自己的網頁計算器了。