在编程语言中,数值总会有个边界,JavaScript 也不例外,我们通过下面的代码可以看到边界:
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
Number.MAX_SAFE_INTEGER //9007199254740991
Number.MIN_SAFE_INTEGER //-9007199254740991
然而,如果要对超大
的数值进行相加,就不能依靠传统的 +
了,因为超过 Number.MAX_SAFE_INTEGER
就会被转换成科学计数,并不是我们想要看到的直观的结果。接下来,我们设计个算法来实现这个加法。
算法思路
- 设计两个入参: a 和 b, 它们的类型可能是:
string | Number
。设计一个返回结果 result。 - 将参数按照字符串的方式,逐字符拆分,从个位(最右边开始),一位一位的相加,如 大于10 则进位。
- 将逐位相加的结果与 result 进行相加,最后得到字符串结果。
这个是大致思路,里面包含了一些细节,接下来通过代码来实现。
代码实现
方案一:比较直观,没有太多代码技巧,但是表达出了思路,面试的话估计可以得个 60分
function bigNumber(a, b) {
// 转成字符串后再计算长度
let i = a.toString().length;
let j = b.toString().length;
let result = "";
let carry = 0; // 进位标识,1 进位
// 从a和b的个位起逐个相加
while (i > 0 || j > 0) {
let x = y = sum = 0;
if (i != 0) {
x = parseInt(a[i - 1]);
i--;
}
if (j != 0) {
y = parseInt(b[j - 1]);
j--;
}
sum = x + y + carry;
// 操过10则进位
if (sum >= 10) {
carry = 1;
sum -= 10;
} else {
carry = 0;
}
// 字符串的形式相加
result = sum + result;
}
if (carry) {
result = carry + result;
}
return result;
};
方案二: 运用了语言的技巧,代码量较少,可扩展的知识点较多。面试的话估计可以得 90分
function bigNumber(a, b) {
// 将字符串转换成数组
let x = a.toString().split("");
let y = b.toString().split("");
let carry = 0; // 进位标识,1 进位
let result = "";
// 从a和b的个位起逐个相加
while (x.length || y.length) {
// ~~x.pop()每次取出数组的最后一个,也就是个位开始
let sum = ~~x.pop() + ~~y.pop() + carry;
carry = sum >= 10 ? 1 : 0;
result = (sum % 10) + result; // 取余再和result相加
}
if (carry) {
result = carry + result;
}
return result;
};
扩展知识点: ~~运算符
从上面的代码可以看出,最让人费解的就是 ~~
运算符。它其实是可以将一些变量转成 Number
类型,适用场景如下:
- 数字类型的字符串可以转换为纯数字:
let a = '999';
~~a; // 999
- 字符串中带了字母、符号或者其他除数字外的,一律转化成 Number 类型的 0
let a = 'abc001';
~~a; // 0
- 布尔类型,如果为true则输出1,false则输出0
let a = true;
~~a; // 1
- 遇到结果是 undefined 的输出 0
let a = undefined;
~~a; // 0