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

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.

This method is designed to throw an exception if s represents an integer that is outside the [Integer.MIN_VALUE, Integer.MAX_VALUE] i.e. [-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)

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.

Related

LeetCode Divide Two Integers - Repeated Exponential Searches solution with negative numbers

I am having hard time understanding below solution in leetcode. Why int powerOfTwo = -1 is initialized with -1 as we have already handled divide(INT_MIN, -1) case
:
Adding problem statement -
Given two integers dividend and divisor, divide two integers without using multiplication, division, and mod operator.
Return the quotient after dividing dividend by divisor.
The integer division should truncate toward zero, which means losing its fractional part. For example, truncate(8.345) = 8 and truncate(-2.7335) = -2.
Note:
Assume we are dealing with an environment that could only store integers within the 32-bit signed integer range: [−2^31, 2^31 − 1]. For this problem, assume that your function returns 2^31 − 1 when the division result overflows.
Example 1:
Input: dividend = 10, divisor = 3
Output: 3
Explanation: 10/3 = truncate(3.33333..) = 3.
https://leetcode.com/problems/divide-two-integers/solution/
private static int HALF_INT_MIN = -1073741824;
public int divide(int dividend, int divisor) {
// Special case: overflow.
if (dividend == Integer.MIN_VALUE && divisor == -1) {
return Integer.MAX_VALUE;
}
/* We need to convert both numbers to negatives.
* Also, we count the number of negatives signs. */
int negatives = 2;
if (dividend > 0) {
negatives--;
dividend = -dividend;
}
if (divisor > 0) {
negatives--;
divisor = -divisor;
}
int quotient = 0;
/* Once the divisor is bigger than the current dividend,
* we can't fit any more copies of the divisor into it. */
while (divisor >= dividend) {
/* We know it'll fit at least once as divivend >= divisor.
* Note: We use a negative powerOfTwo as it's possible we might have
* the case divide(INT_MIN, -1). */
int powerOfTwo = -1;
int value = divisor;
/* Check if double the current value is too big. If not, continue doubling.
* If it is too big, stop doubling and continue with the next step */
while (value >= HALF_INT_MIN && value + value >= dividend) {
value += value;
powerOfTwo += powerOfTwo;
}
// We have been able to subtract divisor another powerOfTwo times.
quotient += powerOfTwo;
// Remove value so far so that we can continue the process with remainder.
dividend -= value;
}
/* If there was originally one negative sign, then
* the quotient remains negative. Otherwise, switch
* it to positive. */
if (negatives != 1) {
return -quotient;
}
return quotient;
}

Efficient Parsing of Byte Array to Number

Rather than parsing a byte array to an ASCII string then converting the string to say an integer, it should be more efficient to parse the byte array directly to an integer.
byte[] token = "24000".getBytes(Charset.forName("US-ASCII"));
The following code can do this:
int n = 0;
for (byte b : token)
n = 10*n + (b-'0');
Versus the common approach:
int n = Integer.parseInt(new String(token));
Ref: Dave's answer here >> Converting US-ASCII encoded byte to integer and back
Is there a comprehensive solution that skips String creation and goes straight to result?
Please stop marking the question for closing due to this question:
Convert a byte array to integer in java and vice versa
It deals with non-encoded bytes.
It does not answer my question.
The Java library doesn't seem to have a dedicated tool for the job, but it sure has enough tools to write one yourself.
In my opinion, if you're worried about performance since turning byte arrays to ints is a bottleneck in your code, then I suggest writing your own solution based on the piece of code you provided. If it isn't, then just use parseInt for easier readability.
In any case, if Java had a tool for doing this, it would use pretty much the same code under the hood. That's pretty much what Integer.parseInt() does (except it covers other bases, negative numbers, and is safer):
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("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;
}

Number overflow in Java

I am tring to implement C/C++ atoi function in Java, below is the code snippet
for (int j = 0; j < s.length(); j++) {
int digit = Character.digit(s.charAt(j), 10);
if (sum < limit/10) {
if (neg) return Integer.MIN_VALUE;
return Integer.MAX_VALUE;
}
sum *= 10;
if (sum < limit + digit) {
if (neg) return Integer.MIN_VALUE;
return Integer.MAX_VALUE;
}
sum -= digit;
}
For line "if (sum < limit + digit) {", which is correct, however, if I use "sum - digit < limit", it will get wrong result, e.g. input "-2147483649", wrong result 2147483647, should be -2147483648.
I figured this out, cuz sum - digit might be overflow, so this come to another question:
int sum = Integer.MAX_VALUE;
System.out.println(sum < Integer.MAX_VALUE + 1);
Why this prints false? What is the behind logic?
Integer.MAX_VALUE + 1 is equal to Integer.MIN_VALUE, which will be more obvious if you look at them in hexadecimal:
Integer.MAX_VALUE = 0x7fffffff
1 = 0x00000001
---------- +
0x80000000
And 0x80000000 is also known as Integer.MIN_VALUE.
Obviously there's no int lower than Integer.MIN_VALUE.
Also, attempting to test whether a number has overflowed by seeing if it's bigger than the biggest possible value is fundamentally misguided. It can't be bigger than the biggest possible value, that's what "biggest possible" implies. Also, you can't take a number, look at it, and determine whether it has overflowed or not, because every number can be the result of a non-overflowing computation (and indeed of just writing it down as a constant) and as the result of a computation that overflowed. You need to know how you got that number.
If you add Integer.MAX_VALUE and 1, then that sum will overflow, so sum won't be less than the resultant "sum", Integer.MIN_VALUE, so it's false.
To get that to work properly, you can cast Integer.MAX_VALUE as a long so overflow won't occur, and the comparison will work properly.
System.out.println(sum < (long) Integer.MAX_VALUE + 1);
Output:
true
FYI: Integer.parseInt(String) does what you are writing.
Why this prints false? What is the behind logic?
System.out.println(sum < Integer.MAX_VALUE + 1);
This prints false, because Integer.MAX_VALUE + 1 == Integer.MIN_VALUE. So this is equivalent with
System.out.println(sum < Integer.MIN_VALUE);
There is nothing smaller than the minimum value.

Atoi in Java for negative values

I am writing an Atoi function in Java. It runs fine for +ve integers. But what I want is when I enter a negative integer it should give me an error. So I tried including continue statement in my class Atoi. The class implemented is:
class Atoi {
int atoi(String tmp) {
int result = 0;
for (int i = 0; i < tmp.length(); i++) {
char digit = (char)(tmp.charAt(i) - '0');
if(digit == '-')
continue;
}
else {
result += (digit * Math.pow(10, (tmp.length() - i - 1)));
}
return result;
}
}
But unfortunately it gives me the negative equivalent of the character i.e for -12 it gives me 655312! Help.
EDIT: Suppose I need to check for floating point numbers what should I do? If I enter 12.1 or 123.2 it should return 12.1 and 123.2 repectively!!
Instead of continue you should give an error (throw an exception, return -1 or whatever you mean with "give an eror").
If you want to ignore the - you can change the else clause to:
result = digit + result * 10;
Quick fix for the obvious problem: the order of the logic was wrong...
Instead of
char digit = (char)(tmp.charAt(i) - '0');
if(digit=='-')
continue;
try
char origChar=tmp.charAt(i);
if(origChar=='-')
continue;
char digit = (char)(origChar - '0');
But there are two more problems:
it does not negate the value, in case of a '-' character is present!
what if this is the input string: -1-2-3-4-5? The result will be interesting! EDIT: try this input also: 'répa'... Even more interesting result!
Don't forget to test with incorrect inputs too, and as #Klaus suggested, don't hesitate to throw an exception, (preferably IllegalArgumentException) with a correct error message, if an incorrect input is given to the function...
If this is not being done as a programming exercise, there is a simpler solution:
static int atoi(String tmp)
{
int result = Integer.parseInt(tmp);
if(result >= 0) {
return result;
} else {
throw new IllegalArgumentException("Negative string "+"\"" + tmp + "\"");
}
}
Substitute the appropriate exception or other action in the negative result case. If you want to just ignore '-', as in the posted code, replace the if-then-else with:
return Math.abs(result);
This code also throws an exception for strings like "abc".
More generally, if a library method does not do exactly what you want, it is often easy to use it in a method that modifies its behavior, rather than re-writing it.
You can write code like this, of course, but you need to check that tmp is a valid number.
int atoi(String tmp) {
int result = 0;
int factor = tmp.charAt(0) == "-" ? -1 : 1;
for (int i = 0; i < tmp.length(); i++) {
if (tmp.chatAt(i) < '0' || tmp.chatAt(i) > '9')
continue;
char digit = (char)(tmp.charAt(i) - '0');
result += (digit * Math.pow(10, (tmp.length() - i - 1)));
}
return result * factor;
}
if(digit=='-')
With
(char)(tmp.charAt(i)
You're code is assuming there are no -'s
(char)(tmp.charAt(i) - '0');
Is an optimization that's blindly clamping the 'digit' variable to a number.
You need to step through what your code is actually doing, search for an ASCII chart and work through what the subtractions of '0' does ('0' == 48), so '1' (49) - '0' (48) = 1 etc...
If you don't want to convert negative numbers then simply return 0 whenever you encounter - sign instead of looping further. Put this code before the if-else block.
if(tmp.charAt(i)=='-')
return 0;

Java recursion and integer double digit

I'm trying to take an integer as a parameter and then use recursion to double each digit in the integer.
For example doubleDigit(3487) would return 33448877.
I'm stuck because I can't figure out how I would read each number in the digit I guess.
To do this using recursion, use the modulus operator (%), dividing by 10 each time and accumulating your resulting string backwards, until you reach the base case (0), where there's nothing left to divide by. In the base case, you just return an empty string.
String doubleDigit(Integer digit) {
if (digit == 0) {
return "";
} else {
Integer thisDigit = digit % 10;
Integer remainingDigits = (digit - thisDigit) / 10;
return doubleDigit(remainingDigits) + thisDigit.toString() + thisDigit.toString();
}
}
If you're looking for a solution which returns an long instead of a String, you can use the following solution below (very similar to Chris', with the assumption of 0 as the base case):
long doubleDigit(long amt) {
if (amt == 0) return 0;
return doubleDigit(amt / 10) * 100 + (amt % 10) * 10 + amt % 10;
}
The function is of course limited by the maximum size of a long in Java.
I did the same question when doing Building Java Programs. Here is my solution which works for negative and positive numbers (and returns 0 for 0).
public static int doubleDigits(int n) {
if (n == 0) {
return 0;
} else {
int lastDigit = n % 10;
return 100 * doubleDigits(n / 10) + 10 * lastDigit + lastDigit;
}
There's no need to use recursion here.
I'm no longer a java guy, but an approximation of the algorithm I might use is this (works in C#, should translate directly to java):
int number = 3487;
int output = 0;
int shift = 1;
while (number > 0) {
int digit = number % 10; // get the least-significant digit
output += ((digit*10) + digit) * shift; // double it, shift it, add it to output
number /= 10; // move to the next digit
shift *= 100; // increase the amount we shift by two digits
}
This solution should work, but now that I've gone to the trouble of writing it, I realise that it is probably clearer to just convert the number to a string and manipulate that. Of course, that will be slower, but you almost certainly don't care about such a small speed difference :)
Edit:
Ok, so you have to use recursion. You already accepted a perfectly fine answer, but here's mine :)
private static long DoubleDigit(long input) {
if (input == 0) return 0; // don't recurse forever!
long digit = input % 10; // extract right-most digit
long doubled = (digit * 10) + digit; // "double" it
long remaining = input / 10; // extract the other digits
return doubled + 100*DoubleDigit(remaining); // recurse to get the result
}
Note I switched to long so it works with a few more digits.
You could get the String.valueOf(doubleDigit) representation of the given integer, then work with Commons StringUtils (easiest, in my opinion) to manipulate the String.
If you need to return another numeric value at that point (as opposed to the newly created/manipulated string) you can just do Integer.valueOf(yourString) or something like that.

Categories