Javascript浮動小数点演算及び比較コード収集整理


以下のコードは自動化されます.http://segmentfault.com/a/1190000000324193
浮動小数点加算:
/**
 **     ,           
 **   :javascript         ,                。               。
 **   :accAdd(arg1,arg2)
 **    :arg1  arg2     
 **/
function accAdd(arg1, arg2) {    
    var r1, r2, m, c;    
    try {
        r1 = arg1.toString().split(".")[1].length;
    } catch (e) {
        r1 = 0;
    }    
    try {
        r2 = arg2.toString().split(".")[1].length;
    } catch (e) {
        r2 = 0;
    }
    c = Math.abs(r1 - r2);
    m = Math.pow(10, Math.max(r1, r2));    
    if (c > 0) {        
        var cm = Math.pow(10, c);        
        if (r1 > r2) {
            arg1 = Number(arg1.toString().replace(".", ""));
            arg2 = Number(arg2.toString().replace(".", "")) * cm;
        } else {
            arg1 = Number(arg1.toString().replace(".", "")) * cm;
            arg2 = Number(arg2.toString().replace(".", ""));
        }
    } else {
        arg1 = Number(arg1.toString().replace(".", ""));
        arg2 = Number(arg2.toString().replace(".", ""));
    }    
    return (arg1 + arg2) / m;
}

// Number      add  ,        。
Number.prototype.add = function (arg) {    
    return accAdd(arg, this);
};
浮動小数点減法:
/**
 **     ,           
 **   :javascript         ,                。               。
 **   :accSub(arg1,arg2)
 **    :arg1  arg2     
 **/
function accSub(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);
}

//  Number      mul  ,        。
Number.prototype.sub = function (arg) {
    return accMul(arg, this);
};
浮動小数点の掛け算:
/**
 **     ,           
 **   :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  ,        。
Number.prototype.div = function (arg) {
    return accDiv(this, arg);
};
以下の内容は以下のとおりです.http://madscript.com/javascript/javscript-float-number-compute-problem/
分析
JavaScriptは一つの数字タイプのNumberだけで、しかもJavascriptの中のすべての数字はIEEE-7054標準形式で表しています.浮動小数点数の精度問題はJavaScript特有のものではなく、一部の小数点はバイナリで桁数を表しているので無限です.
                 
0.1              0.0001 1001 1001 1001 ...
0.2              0.0011 0011 0011 0011 ...
0.3              0.0100 1100 1100 1100 ...
0.4              0.0110 0110 0110 0110 ...
0.5              0.1
0.6              0.1001 1001 1001 1001 ...
したがって、例えば1.1のように、そのプログラムは実際には‘1.1’を表示することができず、ある程度の正確さしかできない.これは避けられない精度損失である.
1.09999999999999999
JavaScriptでは問題はもっと複雑です.ここではChromeでテストしたデータだけをあげます.
                   
1.0-0.9 == 0.1     False
1.0-0.8 == 0.2     False
1.0-0.7 == 0.3     False
1.0-0.6 == 0.4     True
1.0-0.5 == 0.5     True
1.0-0.4 == 0.6     True
1.0-0.3 == 0.7     True
1.0-0.2 == 0.8     True
1.0-0.1 == 0.9     True
現在流行している方法:浮動小数点演算結果を判断する前に計算結果を精度縮小します.精度を縮小する過程で自動的に四捨五入します.
(1.0-0.9).toFixed(digits)                      // toFixed()        0  20   
parseFloat((1.0-0.9).toFixed(10)) === 0.1      //    True
parseFloat((1.0-0.8).toFixed(10)) === 0.2      //    True
parseFloat((1.0-0.7).toFixed(10)) === 0.3      //    True
parseFloat((11.0-11.8).toFixed(10)) === -0.8   //    True
以上の考えに対して行カプセル化する.
//   isEqual            
function isEqual(number1, number2, digits){
    digits = digits == undefined? 10: digits; //      10
    return number1.toFixed(digits) === number2.toFixed(digits);
}

isEqual(1.0-0.7, 0.3);  // return true

//       ,          
Number.prototype.isEqual = function(number, digits){
    digits = digits == undefined? 10: digits; //      10
    return this.toFixed(digits) === number.toFixed(digits);
}

(1.0-0.7).isEqual(0.3); // return true