简体   繁体   中英

Understanding Java Integer.parseInt(String s, int radix) source code

I was looking at the source code for java.lang.Integer's parseInt method.

public static int parseInt(String s, int radix)
            throws NumberFormatException
{
    /*
     * WARNING: This method may be invoked early during VM initialization
     * before IntegerCache is initialized. Care must be taken to not use
     * the valueOf method.
     */

    if (s == null) {
        throw new NumberFormatException("s == null");
    }

    if (radix < Character.MIN_RADIX) {
        throw new NumberFormatException("radix " + radix +
                                        " less than Character.MIN_RADIX");
    }

    if (radix > Character.MAX_RADIX) {
        throw new NumberFormatException("radix " + radix +
                                        " greater than Character.MAX_RADIX");
    }

    int result = 0;
    boolean negative = false;
    int i = 0, len = s.length();
    int limit = -Integer.MAX_VALUE;
    int multmin;
    int digit;

    if (len > 0) {
        char firstChar = s.charAt(0);
        if (firstChar < '0') { // Possible leading "+" or "-"
            if (firstChar == '-') {
                negative = true;
                limit = Integer.MIN_VALUE;
            } else if (firstChar != '+')
                throw NumberFormatException.forInputString(s);

            if (len == 1) // Cannot have lone "+" or "-"
                throw NumberFormatException.forInputString(s);
            i++;
        }
        multmin = limit / radix;
        while (i < len) {
            // Accumulating negatively avoids surprises near MAX_VALUE
            digit = Character.digit(s.charAt(i++),radix);
            if (digit < 0) {
                throw NumberFormatException.forInputString(s);
            }
            if (result < multmin) {
                throw NumberFormatException.forInputString(s);
            }
            result *= radix;
            if (result < limit + digit) {
                throw NumberFormatException.forInputString(s);
            }
            result -= digit;
        }
    } else {
        throw NumberFormatException.forInputString(s);
    }
    return negative ? result : -result;
}

I can see that the multmin is somehow being used to detect Integer overflow on both the negative and positive sides. But I am having a hard time understanding how.

I also do not understand why we are keeping the result negative while calculating and then making it positive at the end if it was not detected as a negative number.

How does multmin work?

multmin is used in below code:

if (result < multmin) {
    throw NumberFormatException.forInputString(s);
}
  • If current result is less than multmin , next generation result must overflow, so an exception is thrown:

    if result < multmin, ------> result < limit / radix (beacause multmin = limit / radix) ------> result * radix < limit ------> result * radix - digit < limit (overflow).

  • If current result is greater than or equals multmin , we can assert result * radix >= limit not overflow, so continue check if result * radix - digit overflow with:

    if (result < limit + digit) { throw NumberFormatException.forInputString(s); }

Why use negative?

Because the absolute value of Integer.MIN_VALUE(-2147483648) is greater than Integer.MAX_VALUE (2147483647) .

Suppose we have a POSITIVE version, when input number start with '+', we can set limit as Integer.MAX_VALUE . But, when input number start with '-', we can not set limit as 2147483648 , it's an overflow value.

This method is designed to throw an exception if s represents an integer that is outside the [Integer.MIN_VALUE, Integer.MAX_VALUE] ie [-2147483648, 2147483647] range.

The algorithm performs repeated multiplication and addition which could eventually lead to overflow. The algorithm avoids overflow by checking the operands in advance.

Checking for overflow

The simplest way of checking if result + digit will cause an overflow without actually adding them is to check:

if (result > limit - digit) // result, limit and digit are positive

The simplest way of checking if result * radix will cause an overflow without actually multiplying them is to check:

if (result > limit / radix) // result, limit and radix are positive

So this explains what limit = Integer.MAX... and multmin = limit / radix do.

Why "accumulating negatively"?

The algorithm separates out the sign and operates on remaining digits (it is easier to deal with one case). One special case it must handle is that of -2147483648 ; in which case the limit must be set to 2147483648 which is outside the range of Integer .

With negative accumulation, the limit could be set to -2147483648 . Note that "if" conditions described above must be adjusted for negative numbers as follows:

if (result < limit + digit) // result and limit are negative
if (result < limit / radix) // result and limit are negative

Here is a rough outline of that happens inside the algorithm at each step:

// parseInt("123", 10)
  limit: -2147483647 (-Integer.MAX_VALUE)
multmin: -214748364
 result: -1
 result: -12
 result: -123

// parseInt("2147483648", 10)
  limit: -2147483647 (-Integer.MAX_VALUE)
multmin: -214748364
 result: -2
 result: -21
 result: -214
 result: -2147
 result: -21474
 result: -214748
 result: -2147483
 result: -21474836
 result: -214748364
 result: Overflow (after multiplication, before subtraction)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM