Vaysym Vaysym - 20 days ago 7
Java Question

Why is the largest testable integer parsed from user input as a string (10^8)-1?

Accepting user input one character at a time, the largest acceptable integer before I have to limit input seems to be (10^8)-1. I was mildly surprised that it wasn't

Integer.MAX_VALUE
. Why isn't it?

Code written out in the
Keyboard
class that
extends KeyAdapter
:

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class Keyboard extends KeyAdapter{
private final int MAX_TESTABLE = 99999999;
private final int VK_NUMBER = 48; //ASCII 0 starts here
private final int VK_NUMBERPAD = 96; //ASCII numberpad offset

public void keyPressed(KeyEvent e){
int key = e.getKeyCode();

if(((char)key == '0' ||
(char)key == '1' ||
(char)key == '2' ||
(char)key == '3' ||
(char)key == '4' ||
(char)key == '5' ||
(char)key == '6' ||
(char)key == '7' ||
(char)key == '8' ||
(char)key == '9')){
if(Integer.parseInt(Launcher.inputString+(char)key) <= MAX_TESTABLE){
Launcher.inputString += (char)key;
}
}else if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD){
if(Integer.parseInt(Launcher.inputString+(char)(VK_NUMBER+key-VK_NUMBERPAD)) <= MAX_TESTABLE){
Launcher.inputString += (char)(VK_NUMBER+key-VK_NUMBERPAD);
}
}

System.out.println(
"Key "+key+" pressed\n"+
"Input string: "+Launcher.inputString
);
}
}


Other classes linked here:


Answer

EDIT: Here is what ended up being the solution, as found by Vaysym. I'm pasting it here to make it easer for anyone in the future who might look this up:

The answer is in the question: (10^9)-1

Why: In Java, the primitive type int is allowed 4 bytes of memory: 2^32 Because it is signed, we have to allocate half of the 32 bits for the negative spectrum. We also have to subtract one from the total (which, because it's an odd number, happens to actually subtract from the positive spectrum). So our total range becomes (2^(32-1))-1 = 2,147,483,647

Because we are testing USING int here, this is the maximum testable number. So on the right operand the most we can check for is (2^31)-1, so we get this code: if(Integer.parseInt(inputString+(char)key) < 2147483647){}

But this will still throw an exception because the left operand can end up being higher than Integer.MAX_VALUE, so we have to limit userInput BEFORE it gets there. Because the input is received one character at a time and the largest digit you can input (9) is greater than the left-most digit of (2^31)-1, which is a 2, the closest we can get to Integer.MAX_VALUE is an entire order of magnitude short; Integer.MAX_VALUE has 10 digits, so we must only test up to 9 digits: (10^(10-1))-1 = (10^9)-1 = 999,999,999.

Final, proper code: if(Integer.parseInt(inputString+(char)key) < 999999999){}

Edit: You have to go 2 orders of magnitude down - not 1

--------------------

Original answer:

The problem with how you're using Integer.parseInt is (quoted from my comment):

If you pass in a value larger then 2147483647 to Integer.parseInt(), it will throw the exception before it can even compare it to < 2147483647, because you're already trying to assign a value to large for a Integer in your call to parseInt().

The exception will be thrown if you pass in anything other then a number under 2147483647, including an empty String.

To get around this, try using a try-catch block:

        try
        {
            Integer.parseInt(inputString);
            //No exception thrown, this is a valid Integer!
        }
        catch(NumberFormatException e)
        {
            //NumberFormatException! "inputString" Can not be an Integer!
        }

If an exception is thrown by Integer.parseInt(), the code in the catch block will run, otherwise the code in the try block will continue running. By catching the exception, it won't cause your program to crash.

If you don't want to use try/catch, you'll just have to limit what the user can type. You can use Long and parseLong instead of Integer to allow larger numbers, but that will still throw an exception if you enter a non-number.

Update: You could use this to check if input string will fit into an Integer (if the number is smaller then a long, which it probably will be), but unfortunately it will still throw the exception if you enter something that isn't a number.

    if(Long.parseLong(inputString+(char)key) > 2147483647)
    {
        //"inputString+(char)key" Will fit into an Integer!
        inputString += (char)key;
    }
    else
    {
        //"inputString+(char)key" Will NOT fit into an Integer!
    }

Update 2: Looking at your edit, you're quite close. What's happening is when you add the char, it is getting added to end of the String, then parseInt is preformed on it. So adding the string "999999999" to the char (let's say it has a value of 1), will equal 9999999991 (or 9,999,999,991 when it's converted to a number) not 1,000,000,000. The number is then larger then an Integer by the time parseInt() is preformed on it.

Also, casting a int to a char will print out the ASCII character corresponding with the int's number, see this question for more on that.

To get the output you're looking for, try casting your inputString and key before adding them together. Example: (With inputString = 50 and key = 50)

Integer.parseInt(inputString) + (int)key    // = 100

Instead of:

Integer.parseInt(inputString+(char)key)      // Would equal 5050, but because 50 is the ASCCI character for 2, it will be 502.

Note that this will still throw an exception if you try to parse a number larger then 2147483647, so please consider enclosing it with a try/catch block.

Hope this helps you understand what's happening.

Comments