LeetCode加一题中parseInt转换超15位数字精度丢失方案问询
问题背景
求解LeetCode「加一」题目时,自研解法通过了绝大多数测试用例,仅1个用例运行失败,该失败用例输入数组为:[6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3]。
题目要求:将输入数组拼接为完整数字后整体加1,再将结果转换回数组格式返回。
原解法逻辑:最终输出前调用parseInt("6145390195186705543")+1完成加1操作,之后将结果转为字符串、拆分后再转回数字数组。运行时发现内置方法parseInt()无法正确转换第15位之后的数字,得到的中间结果为6145390195186705000,第15位之后全部变为0,最终输出错误。
核心疑问:如何将长度超过16位的数字字符串正确转换为Number类型?
补充说明:已尝试过BigInt()方法,理论上该方法可解决大整数精度问题,但在本题场景下使用BigInt()无法得到正确输出,运行结果不符合预期。
原错误实现代码:
var plusOne = function(digits) { let y = digits.map(String); let z = '' for (let i = 0; i < y.length; i++) { z += y[i]; } let a = (parseInt(z) + 1).toString().split('') return a.map(Number) };
问题根因
- JavaScript的
Number类型为IEEE 754标准的64位双精度浮点数,安全整数范围仅为-9007199254740991 ~ 9007199254740991(即±2^53-1),超出这个范围的整数无法被精确存储,这就是parseInt()转换长数字串时15位后全部变0的根本原因。超过安全整数范围的数字字符串转Number类型必然丢失精度,不存在可行的精确转换方案。 - 之前使用
BigInt()未得到正确结果,属于写法错误:一是不能将已经精度丢失的Number值传入BigInt构造函数,必须传入原始完整数字字符串;二是BigInt类型不能直接和普通Number类型做算术运算,加法需要用BigInt类型的字面量1n。
可行修正方案
方案1:正确使用BigInt实现
直接传入拼接好的原始数字字符串构造BigInt,搭配BigInt类型的1完成加法,全程不经过Number类型转换,不会丢失精度:
var plusOne = function(digits) { const numStr = digits.join(''); const result = BigInt(numStr) + 1n; return result.toString().split('').map(Number); };
方案2:数组原生进位实现(完全规避大整数问题)
不需要把数组转成数字,直接从数组末尾开始模拟加1的进位逻辑,性能更好,也完全不存在精度问题:
var plusOne = function(digits) { for (let i = digits.length - 1; i >= 0; i--) { digits[i]++; if (digits[i] !== 10) return digits; digits[i] = 0; } // 处理所有位都进位的场景,比如999 + 1 = 1000 return [1].concat(digits); };
内容的提问来源于stack exchange,提问作者Sai Anvesh Reddy
相关产品推荐
相关产品推荐

