大数求和,支持负数
/**
* 比较大小
* @param {string} a 数字 a
* @param {string} b 数字 b
* @returns 去掉符号后,较大的在前,较小的在后,如果相等则位置三为 true [[biggerSym, bigger], [smallerSym, smaller], isEqual]
*/
function biggerFirst(a, b) {
// 思路:
// 保存两个数字的符号
// 数字转数组,比较两个数大小,先根据长度比,长的更大,如果长度一致,就按位比
// 较大的在前,较小的在后,如果一样大则第三个位置为 true。组成一个数组作为返回值
let aSym = '', bSym = ''; // a 和 b 的符号
if (a[0] === '-') {
aSym = '-';
a = a.substr(1);
}
if (b[0] === '-') {
bSym = '-';
b = b.substr(1);
}
a = a.split('');
b = b.split('');
const equal = () => [[aSym, a], [bSym, b], true];
const big = () => [[aSym, a], [bSym, b], false];
const small = () => [[bSym, b], [aSym, a], false];
if (a.length > b.length) return big();
if (a.length < b.length) return small();
for (let i = 0; i < a.length; i++) {
if (a[i] < b[i]) return small();
if (a[i] > b[i]) return big();
continue;
}
return equal();
}
/**
* 大数求和(支持负数)
* @param {string} s 第一个数字
* @param {string} t 第二个数字
* @returns 两数之和
*/
function sum(s, t) {
// 思路
// 比较两个数去符号后的大小,大的在前小的在后,如果一样大且符号不同,直接返回 0
// 两种情况:
// 1. 符号相同(同正或同负),则两数相加,结果的符号与较大数的符号一致
// 2. 符号不同(一正一负),则大的减小的,结果的符号与较大数的符号一致
// 所以,可以把两种情况合并,把不同处提前定义好。比如:
// 1. 两数相加和相减的不同,可以提前根据符号是否相同来确定 sym 为 -1 还是 1
// 2. 相加和相减的进位条件不同,通过 updateCarry 函数确定判断条件并更新 carry 的值
// 3. carry 取余的差异,可以通过加 10 再取余,解决相加和相减两种情况中取余的差异
// 最后用正则表达式将结果前面的 0 去掉,再根据较大数的符号来决定结果是否需要加负号
// 返回结果
const [[aSym, a], [bSym, b], equal] = biggerFirst(s, t); // 大小比较,较大值优先
const isDiffSym = aSym !== bSym; // 是否异号
if (isDiffSym && equal) return '0'; // 异号且相等,直接返回 0
let result = '', carry = 0; // result 存储结果,carry 存储中间值和进位
// 进位更新函数,根据是否异号有两种更新方案
const updateCarry = (() => {
// 因为 carry 是加过 10 的,所以判断条件都要加 10
if (isDiffSym) return c => c < 10 ? -1 : 0;
return c => c > 19 ? 1 : 0;
})();
// 根据是否异号决定较小的数是加还是减
const sym = isDiffSym ? -1 : 1;
// 两个数从个位开始计算,注意最后的进位
while (a.length || b.length || carry) {
carry += 10 + ~~a.pop() + ~~b.pop() * sym; // ~表示按位取反,~~任何非数字都会得到 0,加 10 是为了模拟减法中的借位且不影响加法的取余
result = carry % 10 + result; // 由于上一步加 10,因此此处加减法可以统一这么算
carry = updateCarry(carry); // 更新进位
}
// 将结果前面的 0 去掉
result = result.replace(/^0+([1-9])/, '$1');
// 结果的符号与较大数符号一致
if (aSym === '-') result = '-' + result;
return result;
}
console.log(sum('-90010', '90000'));