最近突然来了灵感, 想去 LeetCode 刷点算法题, 在一道初级题目上遇到了看似无法理解的错误.
题目:
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数0
之外,这个整数不会以零开头。
- 示例 1:
输入:[1,2,3]
输出:[1,2,4]
解释: 输入数组表示数字123
。- 示例 2:
输入:[4,3,2,1]
输出:[4,3,2,2]
解释: 输入数组表示数字4321
。
简单思考了一下, 考的无非就是数字进位, 但也可以利用数组转数字 => 数字加一 => 数字转数组的方式来绕过进位运算. 于是有了以下 JS 代码:
/**
* @param {number[]} digits
* @return {number[]}
*/
var plusOne = function(digits) {
let newNum = ((Number(digits.join('')) + 1)).toString();
let result = [];
for(let i = 0; i < newNum.length; i++) {
result.push(newNum[i]);
}
return result;
};
提交以后在一个测试用例上出现了问题:
输入:[6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3]
输出:[6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,0,0,0]
预期:[6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,4]
为什么后面几位都变成了 0 呢?
其实在 JS 中, 浮点数运算存在精度误差, 0.1 + 0.2 = 0.30000000000000004
从计算机层面讲, 误差的出现是因为其转为二进制计算后位数过长, 超出部分被截断后转为十进制, 导致出现精度误差.
而本题中后面几位都变成 0 的原因也比较类似: 简单地说, 数字大小超出了单个变量的长度限制, 存储时以类似 1.222e32
的形式存储, 仅保留了长度限制内的精度信息, 取出时长度限制以外的位则默认补 0.
其实 JS 中也内置了最大和最小安全整数值:
console.log(Number.MAX_SAFE_INTEGER); //9007199254740991
console.log(Number.MIN_SAFE_INTEGER); //-9007199254740991
可以看出长度刚好就是错误的输出中没有补 0 的长度.
因此, 在本题中为了满足全体整数的情况, 在处理较大的数字时依然需要通过字符串的方式存储, 这样一来进位运算当然就是不可避免的了.
相关链接:
关于JS精度丢失问题
关于JS最大安全整数
网友评论