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

一、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) 
} 

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351