在JS中, Number 类型使用 IEEE 754格式表示整数和浮点值(在某些语言中也叫双精度值),这也就意味着JavaScript能精确表示的数字是有限的,其可以精确到个位的最大整数是2的53次方,即9007199254740992, 如果数据大小超过了这个范围,就会导致精度缺失,造成JS无法判断大小,同样的,如果我们利用已经缺失了精度的数据去计算,那么得到的结果一样也是精度缺失了的。那么该如何解决呢? 一起来看看下面的解题思路
思路
- 先获取传进来的两个参数中
length最大的那个并用一个变量maxLength保存起来。 - 根据
maxLength的长度,将传进来的两个参数向前补位,举个列子:若参数a是123456789009876,参数b是12345678, 则通过补位后得到的结果将是:参数a:123456789009876, 参数b:000000012345678。至于如何向前补位,方法有很多,能够直接想到的方法就是将字符串转数组然后遍历。这种方法太麻烦,我们可以利用字符串提供的API方法padStart()可以非常方便的解决这个问题,关于padStart()方法会在下面介绍其使用方法。 - 遍历
maxLength,然后依次将两个参数相同位数的值相加,如果相加的和大于等于10则需要向上一位进1,依次往复,直到遍历完成。
对于第三步可能不是很好的理解,下面来看下代码吧,我们利用代码来进一步了解它的实现思路。
代码实现
代码实现第一步
方法一:
这种方法代码量太大,可以直接看第二种方法...
function bigNumberSum(a, b) {
const aLen = a.length
const bLen = b.length
const distance = aLen - bLen
const maxLength = distance > 0 ? aLen : bLen
console.log(maxLength)
}
方法二:
这个方法一行代码就可以搞定了。
function bigNumberSum(a, b) {
const maxLength = Math.max(a.length, b.length)
console.log(maxLength)
}
代码实现第二步
这一步就不写字符串转数组后遍历这种方法了,代码量相对于下面的来说太大了,这里我们直接使用字符串提供的padStart()方法。
字符串的 padStart() 方法 和 padEnd() 方法
这两个方法都会复制字符串,如果小于指定的长度,则会在相应的一边填充字符,直至满足条件。这两个方法的第一个参数是指定的长度,第二个参数是可选的填充字符串,默认是空格。下面来看几个例子
let str = 'hunter'
console.log(str.padStart(9)) // hunter
console.log(str.padStart(9, '0')) // 000hunter
console.log(str.padStart(9, '.')) // ...hunter
console.log(str.padEnd(9)) // hunter
console.log(str.padEnd(9, '0')) // hunter000
console.log(str.padEnd(9, '.')) // hunter...
OK,了解了上面的两个方法之后,我们开实现思路中的第二步,直接上代码:
function bigNumberSum(a, b) {
const maxLength = Math.max(a.length, b.length)
// console.log(maxLength)
a = a.padStart(maxLength, '0')
b = b.padStart(maxLength, '0')
console.log(a) // 132456
console.log(b) // 000089
}
bigNumberSum('132456', '89')
上面这两步都很好理解,我们接着看第三步
代码实现第三步
function bigNumberSum(a, b) {
const maxLength = Math.max(a.length, b.length)
a = a.padStart(maxLength, '0')
b = b.padStart(maxLength, '0')
let temp = 0 // 用来标识是否需要进位 满10进1
let fragment = 0 // 保存相同位数相加得到的值
let sum = ''
for(let i = maxLength - 1; i >= 0; i--) {
// 记录 a和b 相同位数相加的结果, 同时加上temp
// 如果temp为1,那么说明之前的位数相加时 满10了,需要进1,那么到了这一位需要加一
fragment = parseInt(a[i]) + parseInt(b[i]) + temp
// 根据满10进1的原则,这里计算并保存其结果
temp = Math.floor(fragment / 10)
// 这里的fragment % 10 是取余数,即余数是当前遍历到的位置的值,多出来的值会在下一轮循环中加上(要记住满十进一原则)
sum = fragment % 10 + sum
}
// 这里要处理下,如果循环完毕后,temp的结果为1,则说明还需要再向前进一位
if(temp === 1){
sum = "1" + sum;
}
return sum
}
bigNumberSum('9007199254740991', '1234567899999999999') // 1243575099254740990
到这里就完成了一个简单版的大数相加的工具方法。
完成版代码
function bigNumberSum(a, b) {
const maxLength = Math.max(a.length, b.length)
a = a.padStart(maxLength, '0')
b = b.padStart(maxLength, '0')
let temp = 0
let fragment = 0
let sum = ''
for(let i = maxLength - 1; i >= 0; i--) {
fragment = parseInt(a[i]) + parseInt(b[i]) + temp
temp = Math.floor(fragment / 10)
sum = fragment % 10 + sum
}
if(temp === 1){
sum = "1" + sum;
}
return sum
}
总结
以上是我们实现了一个简单版的大数相加的方法,不过需要注意的地方是,传入进来的参数需要是字符串,遗憾的是(我懒)在这一版实现的代码中没有做类型判断, 如果传入了一个非常大的Number类型的数字(比2的53次方大)会出现问题,当然,其实代码里面还有很多地方需要舔砖加瓦,这里就不一一实现了。