处理大型数组时reduce()返回NaN?LeetCode 1822问题排查
LeetCode 1822题:reduce计算乘积返回NaN的原因及解决办法
问题场景
你在解决LeetCode 1822题时遇到了问题:题目要求实现一个signFunc,数值为正返回1、负返回-1、零返回0,最终要返回数组所有元素乘积的signFunc结果。
你的初始代码如下:
var arraySign = function(nums) { return Math.sign(nums.reduce((acc, curr) => acc * curr)); };
小型数组测试正常,但传入包含超大数值的长数组时,结果却返回NaN。你临时改用条件判断的方式规避了问题:
var arraySign = function(nums) { let product = nums.reduce((acc, curr) => acc * curr); if(product>0) return 1; else if (product<0) return -1; else return 0; };
但你想知道:reduce本身有什么限制吗?为什么会出现这种情况?
问题根源
reduce函数本身没有任何限制,问题出在直接计算超大数组元素乘积时触发了JavaScript的数值溢出:
- JavaScript使用双精度浮点数存储Number类型,只能安全表示
-(2^53 - 1)到2^53 - 1之间的整数。当数组元素数量多、数值大时,乘积会超出这个范围,最终变成Infinity或-Infinity。 - 如果数组中同时存在0,
Infinity * 0或-Infinity * 0的计算结果是NaN,这就导致reduce返回NaN,而Math.sign(NaN)自然也返回NaN。 - 你的临时方案其实是靠
else return 0处理了NaN的情况,但这是逻辑漏洞:NaN不等于乘积为0,只是计算溢出导致的异常值,这种处理可能在某些测试用例下得到错误结果。
正确的解决思路
其实根本不需要计算实际乘积——我们只需要关注两个关键信息就能得到结果:
- 数组里有没有0?有就直接返回0;
- 数组里负数的个数是奇数还是偶数?偶数返回1,奇数返回-1。
按这个思路写的代码既不会触发溢出,效率还更高(遇到0可以直接终止遍历):
var arraySign = function(nums) { let negativeCount = 0; for (const num of nums) { if (num === 0) return 0; if (num < 0) negativeCount++; } return negativeCount % 2 === 0 ? 1 : -1; };
内容的提问来源于stack exchange,提问作者Jan Kamoń
相关产品推荐
相关产品推荐

