一、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)
}
另:如果浮点数的小数部分位数已知,可以通过将浮点数放大到整形,运算后在除以放大的倍数,也可得到正确的结果,此方法可用于检测,不推荐使用。
网友评论