Vue或JS的浮點型乘除法無法精確計算


最近做購物車時,遇到了浮點型乘除法無法精確計算,涉及到產品價格的計算,經常會出現JS浮點數精度問題,這個問題,對於財務管理系統的開發者來說,是個非常嚴重的問題(涉及到錢相關的問題都是嚴重的問題),這里把相關的原因和問題的解決方案整理一下,也希望給各位提供一些參考。

一. 常見例子  

// 加法
0.1 + 0.2 = 0.30000000000000004
0.1 + 0.7 = 0.7999999999999999
0.2 + 0.4 = 0.6000000000000001
 
// 減法
0.3 - 0.2 = 0.09999999999999998
1.5 - 1.2 = 0.30000000000000004
 
// 乘法
0.8 * 3 = 2.4000000000000004
19.9 * 100 = 1989.9999999999998
 
// 除法
0.3 / 0.1 = 2.9999999999999996
0.69 / 10 = 0.06899999999999999
 
// 比較
0.1 + 0.2 === 0.3 // false
(0.3 - 0.2) === (0.2 - 0.1) // false

 

二. 導致原因

下面我們來說一下浮點數運算產生誤差的原因:(拿0.1+0.2=0.30000000000000004進行舉例)

首先,我們要站在計算機的角度思考 0.1 + 0.2 這個看似小兒科的問題。我們知道,能被計算機讀懂的是二進制,而不是十進制,所以我們先把 0.1 和 0.2 轉換成二進制看看:  

0.1 => 0.0001 1001 1001 1001…(無限循環)

0.2 => 0.0011 0011 0011 0011…(無限循環)  

上面我們發現0.1和0.2轉化為二進制之后,變成了一個無限循環的數字,這在現實生活中,無限循環我們可以理解,但計算機是不允許無限循環的,對於無限循環的小數,計算機會進行舍入處理。進行雙精度浮點數的小數部分最多支持 52 位,所以兩者相加之后得到這么一串 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 ,因浮點數小數位的限制而截斷的二進制數字這時候,我們再把它轉換為十進制,就成了0.30000000000000004

 

三. 解決方法

Vue浮點型乘法:

Vue.prototype.NumberMul = function(arg1, arg2) {
    var m = 0;
    var s1 = arg1.toString();
    var 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);
}

Vue浮點型加法:

Vue.prototype.NumberAdd = function(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))
    n = (r1 >= r2) ? r1 : r2;
    return ((arg1 * m + arg2 * m) / m).toFixed(n);
}

Vue浮點型減法:

Vue.prototype.NumberSub = function(arg1, arg2) {
    var re1, re2, m, n;
    try {
        re1 = arg1.toString().split(".")[1].length;
    } catch (e) {
    re1 = 0;
    }
    try {
        re2 = arg2.toString().split(".")[1].length;
    } catch (e) {
        re2 = 0;
    }
    m = Math.pow(10, Math.max(re1, re2)); 
    n = (re1 >= re2) ? re1 : re2;
    return ((arg1 * m - arg2 * m) / m).toFixed(n);
}

Vue浮點型除法:

// 除數,被除數, 保留的小數點后的位數
Vue.prototype.NumberDiv = function (arg1,arg2,digit){
    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){}
    r1=Number(arg1.toString().replace(".",""))
    r2=Number(arg2.toString().replace(".",""))
    //獲取小數點后的計算值
   var result= ((r1/r2)*Math.pow(10,t2-t1)).toString()
    var result2=result.split(".")[1];
    result2=result2.substring(0,digit>result2.length?result2.length:digit);
 
    return Number(result.split(".")[0]+"."+result2);
}

使用的話也很簡單(乘法計算

{{NumberMul(0.0058,100)}}

如果是JS的話,也可以這樣使用:

var NumberMul = function(arg1, arg2) {
    var m = 0;
    var s1 = arg1.toString();
    var 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);
}

 

 

 

原文鏈接:https://blog.csdn.net/m0_37948170/article/details/88237208


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM