大数运算

大数指number无法存储的数字,一般用字符串来表示。
以下分别讲述大数的加减乘除运算,运算之间可能有所交叉,例如正数减负数就变成两个正数相加,除法里也用到了减法,所以代码就放在了一起。

function checkIsNumber(numStr) {
  const isNumber = /^-?\d+$/.test(numStr)
  if (!isNumber) {
    throw new Error('param must be a number');
  }
  return isNumber;
}

function isZero(numStr) {
  return /^-?0$/.test(numStr);
}

function isNagative(numStr) {
  return numStr[0] === '-';
}

function transformToNagative(numStr) {
  if (isNagative(numStr)) {
    return numStr;
  }
  return '-' + numStr;
}

function getPositivePart(numStr) {
  if (isNagative(numStr)) {
    return numStr.substr(1);
  }
  return numStr;
}

// 大数加法
function add(numStr1, numStr2) {
  // 需要判断正负
  const isNum1Nagative = isNagative(numStr1);
  const isNum2Nagative = isNagative(numStr2);

  numStr1 = getPositivePart(numStr1);
  numStr2 = getPositivePart(numStr2);

  // 一正一负责需要调减法, 否则还是加法
  if (isNum1Nagative + isNum2Nagative === 1) {
    return sub(isNum1Nagative ? numStr2 : numStr1, isNum1Nagative ? numStr1 : numStr2);
  }

  // 补0以避免后续长度判断
  if (numStr1.length < numStr2.length) {
    numStr1 = '0'.repeat(numStr2.length - numStr1.length) + numStr1
  } else if (numStr2.length < numStr1.length) {
    numStr2 = '0'.repeat(numStr1.length - numStr2.length) + numStr2
  }
  const length = numStr1.length
  let carry = 0
  let result = ''
  // 逐位相加,记录进位
  for (let i = length - 1; i >= 0; i--) {
    const temp = +numStr1[i] + +numStr2[i] + carry;
    result = (temp % 10) + result
    carry = Math.floor(temp / 10);
  }
  // 最后进位要补上
  if (carry) {
    result = carry + result;
  }
  // 负数相加最后补负号
  if (isNum1Nagative && isNum2Nagative) {
    result = transformToNagative(result);
  }
  return result;
}


// 大数乘法
function multiBy(numStr1, numStr2) {
  // 需要判断正负
  const isNum1Nagative = isNagative(numStr1);
  const isNum2Nagative = isNagative(numStr2);

  numStr1 = getPositivePart(numStr1);
  numStr2 = getPositivePart(numStr2);
  let len1 = numStr1.length;
  let len2 = numStr2.length;
  // 结果不会超过len1+len2位
  let result = [];
  // 从右往左计算
  for (let i = len1 - 1; i >= 0; i--) {
    for (let j = len2 - 1; j >= 0; j--) {
      // 每一位可能要经过几次相加
      result[i + j] = (result[i + j] || 0) + +numStr1[i] * +numStr2[j];
    }
  }
  // 要计算进位,不反转从右往左计算也是可以的,但是得处理result[-1]、result[-2]这类情况
  result = result.reverse();
  for (let i = 0; i < result.length; i++) {
    if (result[i] > 10) {
      result[i + 1] = (result[i + 1] || 0) + Math.floor(result[i] / 10);
      result[i] %= 10;
    }
  }
  // 前面反转过,需要再反转回来
  const resultStr = result.reverse().join('');
  if (isNum1Nagative + isNum2Nagative === 1) {
    return transformToNagative(resultStr);
  }
  return resultStr;
}

// 两个正数比较大小
function bigger(numStr1, numStr2) {
  const len1 = numStr1.length;
  const len2 = numStr2.length;
  return len1 > len2 || (len1 === len2 && numStr1 >= numStr2)
}

function sub(numStr1, numStr2) {
  // 需要判断正负
  const isNum1Nagative = isNagative(numStr1);
  const isNum2Nagative = isNagative(numStr2);

  numStr1 = getPositivePart(numStr1);
  numStr2 = getPositivePart(numStr2);

  if (isNum1Nagative && !isNum2Nagative) {
    // 负数减正数,等于两个正数相加再取负
    return transformToNagative(add(numStr1, numStr2));
  } else if (isNum1Nagative && isNum2Nagative) {
    // 负数减负数,等于2-1
    return sub(numStr2, numStr1);
  } else if (!isNum1Nagative && isNum2Nagative) {
    // 正数减负数,等于两个正数相加
    return add(numStr1, numStr2);
  }
  // 小减大直接用大数减小数,再加上负号
  if (!bigger(numStr1, numStr2)) {
    return transformToNagative(sub(numStr2, numStr1));
  }
  // 补0
  if (numStr2.length < numStr1.length) {
    numStr2 = '0'.repeat(numStr1.length - numStr2.length) + numStr2
  }
  let carry = 0;
  let result = ''
  for(i = numStr1.length - 1; i >= 0; i--) {
    let current = numStr1[i] - carry;
    
    if (current < +numStr2[i]) {
      carry = 1;
      // 借位
      current += 10;
    } else {
      carry = 0;
    }
    result = (current - numStr2[i]) + result;
  }
  // 去掉前面的0
  let zeroLen = 0;
  for (let i = 0; i < result.length; i++) {
    if (result[i] === '0') {
      zeroLen++
    } else {
      break;
    }
  }
  if (zeroLen > 0) {
    result = result.substr(zeroLen);
  }
  return result;
}

function divide(numStr1, numStr2) {
  if (isZero(numStr2)) {
    throw new Error('dividend can not be zero');
  }
  // 需要判断正负
  const isNum1Nagative = isNagative(numStr1);
  const isNum2Nagative = isNagative(numStr2);
  // 一正一负则结果是负
  const resultIsNagative = isNum1Nagative + isNum2Nagative === 1;

  numStr1 = getPositivePart(numStr1);
  numStr2 = getPositivePart(numStr2);
  const len1 = numStr1.length;
  const len2 = numStr2.length;
  // 除数比被除数小,直接返回
  if (!bigger(numStr1, numStr2)) {
    return {
      integer: '0',
      remainder: numStr1,
    }
  }
  let remainder = '0';
  let integer = ''
  // 截一部分出来做除数
  let divider = numStr1.substr(0, len2);
  numStr1 = numStr1.substr(len2);
  do {
    // 当前位结果计数,和最终结果不一样,最终结果有可能溢出,只能用字符串表示,这个只会一位
    let currentInteger = 0;

    // 除数比较小,检查是否已到最后,如果到了最后,直接返回
    if (!bigger(divider, numStr2)) {
      if (!numStr1) {
        remainder = divider;
        break;
      } else {
        // 否则再取一位
        divider += numStr1[0];
        numStr1 = numStr1.substr(1);
      }
      
    }
    while(bigger(divider, numStr2)) {
      currentInteger++;
      divider = sub(divider, numStr2);
    }
    remainder = divider;
    integer += currentInteger; 
  } while(numStr1)
  if (!integer) {
    integer = '0'
  }
  if (resultIsNagative && integer !== '0') {
    integer = transformToNagative(integer)
  }
  return {
    integer,
    remainder,
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值