美文网首页
js大数运算,浮点型运算,精度问题

js大数运算,浮点型运算,精度问题

作者: 沙克阿拉卡 | 来源:发表于2020-07-24 18:20 被阅读0次

    一、js大数运算

           js大数运算的精度问题,以太坊最小单位精度限制是10的18次方,由于js代码处理超过16位的数字时会损失精度,这个时候可以用大整数字符串运算来得出数字字符串,用于运算。记录于此。

    可以用字符串的方法进行相加,代码如下:

    function sumStrings(a,b){
        var res='', c=0;
        a = a.split('');
        b = b.split('');
        while (a.length || b.length || c){
            c += ~~a.pop() + ~~b.pop();
            res = c % 10 + res;
            c = c>9;
        }
        return res.replace(/^0+/,'');
    }
    
    sumStrings('99999999999999999','9'); // "100000000000000008"
    

    字符串进行相减,代码如下:

    function minusString(a, b){
        a = a.split('');
        b = b.split('');
        var aMaxb = a.length > b.length; // 标记A是否大于B
        if(a.length == b.length)
        {
            for(var i=0, len=a.length; i<len; i++)
            {
                if(a[i] == b[i]) continue;
                aMaxb = a[i] > b[i];
                break;
            }
        }
        if(!aMaxb){
            a = [b, b = a][0]; // 只要A不比B大都交换
        } 
        var result = '';
        while(a.length)
        {
            var temp = parseInt(a.pop()) - parseInt(b.pop() || 0);
            if(temp >= 0) {
                result = temp + result;
            } else{
                result = temp + 10 + result;
                a[a.length-1]--; // 由于已经保证了a一定大于等于b,所以不存在a[i-1]为undefined的情况
            }
        }
        var resultEnd = (aMaxb?'':'-') + result.replace(/^0*/g, '');// 去掉前面可能的无效0
        if(resultEnd === '-'){
            resultEnd = '0'
        }
        return resultEnd; 
    }
    
    minusString('100000000000000000', '8')//"99999999999999992"
    minusString('99999999999999992', '100000000000000000')//"-8"
    

    大整数相乘:

    function multiString(a,b){
       var str1,str2,len1,len2,maxlen,result = [];
        str1 = a.split("").reverse();
        str2 = b.split("").reverse();
        len1 = str1.length;
        len2 = str2.length;
    
        for(var i = 0;i < len1;i++){
            for(var j = 0;j < len2;j++){
                result[i + j] = 0;
            }
        }
        for(var i = 0;i < len1;i++){
            for(var j = 0;j < len2;j++){
                result[i + j] += parseInt(str1[i]) * parseInt(str2[j]);
            }
        }
        var n = result.length;
        for(var k = 0;k < n;k++){
            var temp = result[k];
            if(temp >= 10)
            {
                result[k] = temp % 10;
                if(result[k + 1] === undefined){
                    result[k + 1] = 0;
                }
                result[k + 1] +=  Math.floor(temp / 10);
            }
        }
        return result.reverse().join("");
    }
    
    multiString('50000000000000000','2')//"100000000000000000"
    

    字符串乘以10的n次方:

    function multiPowerString(str,s){
        var resData = '';
        if(str&&str!==''){
            var strArr = str.split('.');
            if(strArr.length===1){
                resData = strArr[0];
                for(var i=0;i<s;i++){
                    resData = resData + '0';
                }
            }else if(strArr.length===2){
                var l = strArr[1].length;
                var cha = l-s;
                if(cha>=0){
                    resData = strArr[1];
                    resData = resData.substring(0,l-cha)+'.'+resData.substring(l-cha);
                    resData = strArr[0] + resData;
                }else{
                    resData = strArr[1];
                    for(var i=0;i<-cha;i++){
                        resData = resData + '0';
                    }
                    resData = strArr[0] + resData;
                }
            }
            //去除多余的0
            var res_ = resData;
            if(res_.indexOf('.')!==-1){
                for(var i=0;i<res_.length;i++){
                    if(res_[res_.length-1-i]=='0'){
                        resData = res_.slice(0,res_.length-1-i);
                    }else if(res_[res_.length-1-i]=='.'){
                        resData = res_.slice(0,res_.length-1-i);
                        break
                    }else{
                        break
                  }
               }
            }
            res_ = resData;
            for(var i=0;i<res_.length;i++){
                if(res_[i]=='0'){
                    resData = res_.slice(i+1);
                }else if(res_[i]=='.'){
                    resData = '0'+res_.slice(i);
                    break;
                }else{
                    break;
                }
            }
        }
        return resData;
    }
    
    multiPowerString('1234567',10)//"12345670000000000"
    multiPowerString('0.1234567',2)//12.34567
    

    字符串除以10的n次方:

     function dividePowerString(num,s){
         var l = num.length;//获取字符串长度
         var res = '';
         if(l==(s+1)){
             res = num;
             res = res.slice(0,1) + '.' + res.slice(1);;
         }else if(l<(s+1)){
             var zero = '';
             while(s - l !== zero.length){
             zero = zero + '0';
             }
             res = '0.'+zero+num;
         }else if(l>=(s+1)){
             var a = l-s;
             res = num;
             res = res.slice(0,a) + '.' + res.slice(a);
         }
         //去多余的0
         var res_ = res;
         for(var i=0;i<res_.length;i++){
             if(res_[res_.length-1-i]=='0'){
                res = res_.slice(0,res_.length-1-i);
             }else if(res_[res_.length-1-i]=='.'){
                res = res_.slice(0,res_.length-1-i);
                break
             }else{
                break
             }
         }
         return res;
     }
    
    dividePowerString('1234567',18)//"0.000000000001234567"
    

    精度缺失导致大整数在十六进制转换上也会有精度缺失。下面的方法来转换成十六进制字符串。

    function str2hex(str){
        var dec = str.toString().split(''), sum = [], hex = [], i, s
        while(dec.length){
            s = 1 * dec.shift()
            for(i = 0; s || i < sum.length; i++){
                s += (sum[i] || 0) * 10
                sum[i] = s % 16
                s = (s - sum[i]) / 16
            }
        }
        while(sum.length){
            hex.push(sum.pop().toString(16))
        }
        return hex.join('')
    }
    
    str2hex('100000000000000000')//"16345785d8a0000"
    

    二、js浮点数运算

           大多数语言在处理浮点数的时候都会遇到精度问题,换算比特币余额的时候又遇到了。比特币最小单位聪的精度限制是10的8次方,转账0.0003的时候,js中0.0003*100000000 会等于29999.999999999996,出现精度问题。

    以下js自定义函数,直接调用即可:
    //说明: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 
    } 
    
    //减法函数
    //调用: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));
        //动态控制精度长度
        n=(r1>=r2)?r1:r2;
        return ((arg1*m-arg2*m)/m).toFixed(n);
    }
    
    //除法函数
    //调用: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); 
        } 
    } 
    
    //乘法函数
    //调用: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) 
    } 
    

    另:如果浮点数的小数部分位数已知,可以通过将浮点数放大到整形,运算后在除以放大的倍数,也可得到正确的结果,此方法可用于检测,不推荐使用。

    相关文章

      网友评论

          本文标题:js大数运算,浮点型运算,精度问题

          本文链接:https://www.haomeiwen.com/subject/kqjslktx.html