訪問http://qinshenxue.com/article.aspx?id=1查看最新文章
說明
眾所周知,js在計算浮點數時候,結果可能會不準確。比如:(在chrome中的運算結果)
2.2 + 2.1 = 4.300000000000001
2.2 - 1.9 = 0.30000000000000027
2.2 * 2.2 = 4.840000000000001
2.1 / 0.3 = 7.000000000000001 網上流傳的代碼(有bug)
網上流傳的最佳化後的代碼如下(有問題的代碼,請勿使用)
function add(a, b) { var c, d, e; try { c = a.toString().split(".")[1].length; } catch (f) { c = 0; } try { d = b.toString().split(".")[1].length; } catch (f) { d = 0; } return e = Math.pow(10, Math.max(c, d)), (a * e + b * e) / e;}function sub(a, b) { var c, d, e; try { c = a.toString().split(".")[1].length; } catch (f) { c = 0; } try { d = b.toString().split(".")[1].length; } catch (f) { d = 0; } return e = Math.pow(10, Math.max(c, d)), (a * e - b * e) / e;}function mul(a, b) { var c = 0, d = a.toString(), e = b.toString(); try { c += d.split(".")[1].length; } catch (f) {} try { c += e.split(".")[1].length; } catch (f) {} return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);}function div(a, b) { var c, d, e = 0, f = 0; try { e = a.toString().split(".")[1].length; } catch (g) {} try { f = b.toString().split(".")[1].length; } catch (g) {} return c = Number(a.toString().replace(".", "")), d = Number(b.toString().replace(".", "")), c / d * Math.pow(10, f - e);}
原理就是將浮點數轉化為整數來計算。 問題代碼測試
但是上面的最佳化方法真的解決問題了嗎,我們可以簡單做下測試。
測試代碼如下:(測試運算中加法)
function test(){ var a = (Math.random() * 100).toFixed(2) - 0; var b = (Math.random() * 1000).toFixed(2) - 0; var result = add(a, b); if ((result + '').length > 10) { console.error('被加數:'+a,'加數:'+b, '結果:'+result); return; } setTimeout(function() { test(); }, 10);}test();
問題代碼測試結果
瀏覽器控制台很快就列印了結果,說明被測試的加法運算代碼存在運算不準確的問題。
測試回合結果:
問題代碼出錯原因
既然上面的代碼有問題,那麼出錯的點在哪裡,我們就以計算錯誤的數字來調試代碼。
被加數:19.36 加數:601.19 結果:620.5500000000001
調試的過程及結果如下:
我們發現原來是其中的乘法計算錯誤。 修正方法
網上有一些版本你會發現在最終返回結果的時候加上了toFixed,這是一種解決方案。
另外既然是乘法出錯,我們何不將乘法換成最佳化後的乘法了。比如修改後的加法如下:
function add(a, b) { var c, d, e; try { c = a.toString().split(".")[1].length; } catch (f) { c = 0; } try { d = b.toString().split(".")[1].length; } catch (f) { d = 0; } return e = Math.pow(10, Math.max(c, d)), (mul(a, e) + mul(b, e)) / e;}
然後用上面的方法進行測試,等了”一天“,控制台也沒列印了。說明這次真的把問題解決了。 最終版(正確版)
function add(a, b) { var c, d, e; try { c = a.toString().split(".")[1].length; } catch (f) { c = 0; } try { d = b.toString().split(".")[1].length; } catch (f) { d = 0; } return e = Math.pow(10, Math.max(c, d)), (mul(a, e) + mul(b, e)) / e;}function sub(a, b) { var c, d, e; try { c = a.toString().split(".")[1].length; } catch (f) { c = 0; } try { d = b.toString().split(".")[1].length; } catch (f) { d = 0; } return e = Math.pow(10, Math.max(c, d)), (mul(a, e) - mul(b, e)) / e;}function mul(a, b) { var c = 0, d = a.toString(), e = b.toString(); try { c += d.split(".")[1].length; } catch (f) {} try { c += e.split(".")[1].length; } catch (f) {} return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);}function div(a, b) { var c, d, e = 0, f = 0; try { e = a.toString().split(".")[1].length; } catch (g) {} try { f = b.toString().split(".")[1].length; } catch (g) {} return c = Number(a.toString().replace(".", "")), d = Number(b.toString().replace(".", "")), mul(c / d, Math.pow(10, f - e));}