Java中LeetCode字符串转整数(Integer)溢出检查原理解析
我正在LeetCode上完成《String to Integer(atoi)》题目,需要实现myAtoi(string s)函数,将字符串转换为32位有符号整数(类似C/C++的atoi函数)。题目要求的算法步骤如下:
- 读取并忽略所有前导空格;
- 检查下一个字符是否为'-'或'+'以确定结果正负,默认正;
- 读取后续数字直到遇到非数字字符或输入结束,其余字符串忽略;
- 将读取到的数字转为整数,未读取到数字则结果为0,再调整符号;
- 若整数超出32位有符号整数范围
[-2^31, 2^31-1],则截断至该范围的边界值; - 返回最终整数。
注意:仅空格' '视为空白字符。
多数解法都是Python版本,我不清楚Java中Integer溢出检查的实现方式,看了在线Java解法后还是不理解其中的溢出检查逻辑,希望有人帮忙解释这段代码的工作原理。以下是该Java版本代码:
class Solution { public int myAtoi(String input) { int sign = 1; int result = 0; int index = 0; int n = input.length(); // 跳过开头所有空格 while (index < n && input.charAt(index) == ' ') { index++; } // 确定正负符号 if (index < n && input.charAt(index) == '+') { sign = 1; index++; } else if (index < n && input.charAt(index) == '-') { sign = -1; index++; } // 遍历后续数字,遇到非数字则停止 while (index < n && Character.isDigit(input.charAt(index))) { int digit = input.charAt(index) - '0'; // 检查溢出/下溢条件 if ((result > Integer.MAX_VALUE / 10) || (result == Integer.MAX_VALUE / 10 && digit > Integer.MAX_VALUE % 10)) { // 溢出返回MAX_VALUE,下溢返回MIN_VALUE return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; } // 将当前数字追加到结果中 result = 10 * result + digit; index++; } // 返回带符号的结果 return sign * result; } }
溢出检查逻辑详解
首先明确32位有符号整数的边界值:
Integer.MAX_VALUE = 2^31 - 1 = 2147483647Integer.MIN_VALUE = -2^31 = -2147483648
代码中在每次将新数字加入结果前,都会先执行溢出检查,避免计算时直接触发溢出导致数值异常。下面拆解判断条件的两种情况:
情况1:result > Integer.MAX_VALUE / 10
Integer.MAX_VALUE / 10的计算结果是214748364(2147483647除以10取整)。如果当前result已经大于这个值,那么result * 10之后必然会超过2147483647——比如result是214748365,乘以10得到2147483650,远大于MAX_VALUE,不管后面加的数字是0-9中的哪一个,都会触发溢出。
情况2:result == Integer.MAX_VALUE / 10 && digit > Integer.MAX_VALUE % 10
当result刚好等于214748364时,result * 10得到2147483640,这时候要看新增的digit是多少。Integer.MAX_VALUE % 10是7(2147483647的最后一位数字),如果digit大于7(也就是8或9),那么10 * result + digit就会变成2147483648或2147483649,超过MAX_VALUE,触发溢出。
为什么不用负数直接判断?
代码里只用MAX_VALUE做判断,再根据sign返回对应边界值,是因为:
负数的边界Integer.MIN_VALUE绝对值是2147483648,比MAX_VALUE大1。当result是214748364且digit是8时,如果sign是-1,最终结果是-2147483648,刚好等于MIN_VALUE;如果digit是9,结果是-2147483649,超出范围,应该返回MIN_VALUE。而上面的判断逻辑刚好覆盖这种场景:只要触发条件,正数返回MAX_VALUE,负数返回MIN_VALUE,完美适配两种溢出情况。
这种提前检查的方式,避免了先计算10*result + digit导致的溢出(Java中整数溢出会按补码循环,溢出后数值会变成负数,反而难以判断),是更安全的溢出处理方式。
内容的提问来源于stack exchange,提问作者Spindoctor

