JS整数的精度是Math.pow(2,53),大于 9007199254740992 (16位数)的可能会丢失精度。所以对于大数字加减和比较,可以将数字拆分成多个15位数字,进行加减或比较。
数字拆分
function getMidNum(str, start, len) {
if (start + len > 0) {
return +str.substr(start < 0 ? 0 : start, start < 0 ? start + len : len)
} else {
return 0
}
}
数字相加
**
* js分治算法实现大整数相加,算法复杂度为O(n/15)
* 处理情况:正数 + 正数、负数+负数、负数+正数、正数+负数
* @param {String} a
* @param {String} b
* @returns {String}
*/
export const bigNumAdd = (a, b) => {
if (a[0] === '-' && b[0] === '-') {
return '-' + bigNumAdd(a.substr(1), b.substr(1))
} else if (a[0] === '-') {
return bigNumSub(b, a.substr(1))
} else if (b[0] === '-') {
return bigNumSub(a, b.substr(1))
}
let res = ''
let temp = 0
let len1 = a.length
let len2 = b.length
let n = Math.ceil(Math.max(len1, len2) / 15)
//分成多少段
for (let i = 1; i < n + 1; i++) {
let num1 = getMidNum(a, len1 - i * 15, 15)
let num2 = getMidNum(b, len2 - i * 15, 15)
//15位数字相加
let strTemp = String(temp + num1 + num2)
if (i !== n) {
//异常1:处理000000000000000+整数的异常情况
if (strTemp.length < 15) {
strTemp = '0'.repeat(15 - strTemp.length) + strTemp
res = strTemp + res
temp = 0
continue
}
//异常2:15位相加等于16位
if (strTemp.length === 16) {
res = strTemp.substr(1, 15) + res
temp = 1
continue
}
}
//相加后的结果放入res前面
res = strTemp + res
temp = 0
}
return res
}
数字相减
/**
* 大整数相减,处理 负数-负数、负数-正数、正数-负数的情况
* @param {String} a
* @param {String} b
* @returns {String}
*/
export const bigNumSub = (a, b) => {
if (a[0] === '-' && b[0] === '-') {
return bigNumSub(b.substr(1), a.substr(1))
} else if (a[0] === '-') {
return '-' + bigNumAdd(a.substr(1), b)
} else if (b[0] === '-') {
return bigNumAdd(a, b.substr(1))
}
let symb = ''
if (bigNumCompare(a, b) < 0) {
symb = '-'
let cache = a
a = b
b = cache
}
let res = ''
let temp = 0
let n = Math.ceil(a.length / 15)
//分成多少段
for (let i = 1; i < n + 1; i++) {
let num1 = getMidNum(a, a.length - i * 15, 15)
let num2 = getMidNum(b, b.length - i * 15, 15)
//15位数字相减
let tempNum = num1 - num2 - temp
if (tempNum < 0) {
temp = 1
tempNum = 1000000000000000 + tempNum
} else {
temp = 0
}
let strTemp = String(tempNum)
if (i !== n) {
//异常1:处理000000000000000+整数的异常情况
if (strTemp.length < 15) {
strTemp = '0'.repeat(15 - strTemp.length) + strTemp
}
}
//相加后的结果放入res前面
res = strTemp + res
}
return symb + res
}
数字比较
/**
* 比较两个大整数的大小,返回-1,0,1 a<b返回-1
* @param {String} a
* @param {String} b
* @returns {number}
*/
export const bigNumCompare = (a, b) => {
let back = 0
let max = Math.ceil(Math.max(a.length, b.length) / 15)
//分成多少段,从左边开始
for (let i = max; i > 0; i--) {
let num1 = getMidNum(a, a.length - i * 15, 15)
let num2 = getMidNum(b, b.length - i * 15, 15)
//15位数字相减
let cur = num1 - num2
if (cur < 0) {
back = -1
break
} else if (cur > 0) {
back = 1
break
}
}
return back
}