以前雖然有看到過 js 精度相關的文章。但也都沒有「印象深入」 ,可是今天"有幸"遇到了。javascript
作一個項目,進行頁面調試的時候,html
當數量增長到3時總價格變得好長好長java
立馬在控制檯驗證了一下,算出這麼多個小數。git
還好以前有看過這方面的文章,知道是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方法,調用起來更加方便。
View Code
JS中toFixed()方法的問題及解決方案
四捨五入失效。ide
《浮點數精度問題的前世此生?爲何計算機會 丟失小數……》函數
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>
View Code
下面的文章均來源於網絡。
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);
}
View Code
可是有的時候,咱們須要計算一連串的公式,而且裏面包含了括號等等的複雜的符合運算,這個時候我們應該怎麼辦呢?
例如:計算(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。同時也能夠製做本身的網頁計算器了。