每日进步:大整数相加

每日进步:大整数相加

背景

在当今的前端开发行业中,随着技术的不断更新和发展,也有越来越多的人进入了这个行业,但同时也带来了更加激烈的竞争。
至5月12日裸辞距今也有一个月,现在找工作也有大半个月了,boss上投了也沟通了不下200家公司,但是大部分都是已读不回,面试邀约只有寥寥3家,技术面过了两家,但可能由于自己的问题最后都不了了之。在继续投简历的空闲时间,琢磨着记录前端知识点。

腾讯面试题

接下来我们来看一道面试题,这道面试题呢,整体难度不大。

1
2
3
4
5
6
7
8
9
10
11
/**
* 腾讯面试题
* 两个超过整数存储范围的最大整数之和
* @description
* @author Leon9916
* @param {String} a
* @param {String} b
*/
function sum(a, b) {

}

这道题再说什么?就是说让咱写一个函数来计算2个大整数之和。
这里,首先给大家科普一下,为什么要写个函数来求和,直接加不就完了吗?

浏览器数字上限

浏览器数字上限通常是指浏览器在 JavaScript 中表示数字时的最大值。由于 JavaScript 中的数字是以 IEEE 754 标准规范表现的,因此在 JavaScript 中,数字类型是使用 64 位浮点数来表示。这意味着 JavaScript 中最大安全整数(Maximum safe integer)为 2^53-1,也就是 9007199254740991,如果大于这个数就会精度出现错误。

我们可以浏览器控制台打印

1
Number.MAX_SAFE_INTEGER // 9007199254740991

当我们超过这个值的话再进行运算,他就有可能不精确
比如我们给他加个1

1
Number.MAX_SAFE_INTEGER + 1 // 9007199254740992

我们再给他的值加给2

1
Number.MAX_SAFE_INTEGER + 2 // 9007199254740992

但是值还是992,这个时候精度就出现问题了

回到问题本身

上面是问题是需要我们写一个函数来手动的完成大精度数字的相加。

1
1131123121323223121312321323121321312 // 1.1311231213232232e+36

上图打印一个超出精度许多的数字,虽然有返回,但是中间许多信息都被省略了
所以,这个函数要接受的2个大于精度的参数,那就只能是字符串,不能写数字了,当然返回结果也只能是字符串了。

接下来就需要一些小学数学知识了

比如我199+20,按照小学体育老师教的方法,从个位开始,加到十位,加到百位 …,依次往前面加,所以说这是一个什么反向的循环。
但是我们循环之前,需要把这些数字对其,不能直接就开始反向循环。

数字对其

首先要做的第一件事就是把这个数字对齐,不足的位数前面补零。因此,首先要拿到2个字符串的最大长度,然后将小的一方的前面用0补齐。

1
2
3
4
5
function sum(a, b) {
const len = Math.max(a.length, b.length); // 获取字符串中的最大长度
a = a.padStart(len, '0') // padStart:往字符串前面添加'0',直到字符串长度为len
b = b.padStart(len, '0') // 同上,因为要保持字符串长度一致,参考小学数学加法
}

上下计算过程中,两个数字相加可能会大于10,所以就需要进位。

1
2
3
4
5
6
7
8
9
function sum(a, b) {
const len = Math.max(a.length, b.length); // 获取字符串中的最大长度
a = a.padStart(len, '0') // padStart:往字符串前面添加'0',直到字符串长度为len
b = b.padStart(len, '0') // 同上,因为要保持字符串长度一致,参考小学数学加法
let carry = 0; // 进位数
for (let i = len - 1; i >= 0; i--) {

}
}

然后从最后开始往前面加,字符串转换成数字并加上进位数,默认最开始加的进位数是0

1
const sum = +a[i] + +b[i] + carry

由于两数相加再加上进位可能会大于9,这时就要向前进一位,并且sum在赋值的时候只用保留个位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function sum(a, b) {
const len = Math.max(a.length, b.length); // 获取字符串中的最大长度
a = a.padStart(len, '0') // padStart:往字符串前面添加'0',直到字符串长度为len
b = b.padStart(len, '0') // 同上,因为要保持字符串长度一致,参考小学数学加法
let carry = 0; // 进位数
let result = ''; // 返回结果
for (let i = len - 1; i >= 0; i--) { // 反向循环
const sum = +a[i] + +b[i] + carry
result = (sum % 10) + result; // 取余,并加上上次的result
carry = Math.floor(sum / 10); // 判断是否需要进位,向下取整,0.9取0, 1.9取1
// if(sum > 9) {
// carry = 1
// } else {
// carry = 0
// } // 向下取整,0.9取0,1.9取1,同上,只是上面的简洁一点
}
return result
}

是不是以为到这就结束了?这个时候还需要判断最后一次计算的时候是不是还需要进位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function sum(a, b) {
const len = Math.max(a.length, b.length); // 获取字符串中的最大长度
a = a.padStart(len, '0') // padStart:往字符串前面添加'0',直到字符串长度为len
b = b.padStart(len, '0') // 同上,因为要保持字符串长度一致,参考小学数学加法
let carry = 0; // 进位数
let result = ''; // 返回结果
for (let i = len - 1; i >= 0; i--) { // 反向循环
const sum = +a[i] + +b[i] + carry
result = (sum % 10) + result; // 取余,并加上上次的result
carry = Math.floor(sum / 10); // 向下取整,0.9取0,1.9取1
// if(sum > 9) {
// carry = 1
// } else {
// carry = 0
// } // 向下取整,0.9取0,1.9取1,同上,只是上面的简洁一点
}
// 循环结束需要判断最后一次是否还存在进位,例子99+99,两位数加上两位数,结果是三位数198,这时如果按照上图的方法就会返回98
if(carry) {
result = carry + result
}
return result
}

控制台打印

1
2
sum('111111222222223333333333333399','22222222222223333333333333399')
// '133333444444446666666666666798'
# 推荐文章
  1.卡片翻动效果
  2.每日进步:一篇文章带你走进3D的世界
  3.每日进步:动画的暂停与恢复
  4.每日进步:大整数相加
  5.响应式原理:Vue 3 的 nextTick ?

:D 舔狗日记获取中...