93

I have the following code...

int Val=-32768;
String Hex=Integer.toHexString(Val);

This equates to ffff8000

int FirstAttempt=Integer.parseInt(Hex,16); // Error "Invalid Int"
int SecondAttempt=Integer.decode("0x"+Hex);  // Error "Invalid Int"

So, initially, it converts the value -32768 into a hex string ffff8000, but then it can't convert the hex string back into an Integer.

In .Net it works as I'd expect, and returns -32768.

I know that I could write my own little method to convert this myself, but I'm just wondering if I'm missing something, or if this is genuinely a bug?

2
  • 1
    possible duplicate of Java negative int to hex and back fails Commented Aug 17, 2012 at 12:17
  • 7
    Just a hint: As convention variable names start with a lower case character: int firstAttempt = 5; Commented Aug 17, 2012 at 12:20

11 Answers 11

85
int val = -32768;
String hex = Integer.toHexString(val);

int parsedResult = (int) Long.parseLong(hex, 16);
System.out.println(parsedResult);

That's how you can do it.

The reason why it doesn't work your way: Integer.parseInt takes a signed int, while toHexString produces an unsigned result. So if you insert something higher than 0x7FFFFFF, an error will be thrown automatically. If you parse it as long instead, it will still be signed. But when you cast it back to int, it will overflow to the correct value.

Sign up to request clarification or add additional context in comments.

Comments

53

It overflows, because the number is negative.

Try this and it will work:

int n = (int) Long.parseLong("ffff8000", 16);

4 Comments

Thanks roni, that seems to be the best solution. Although it still seems odd that Int.parseInt doesn't work as I'd expect.
ffff8000 doesn't fit into an int (bigger then max int), this is a positive number (it is a string so it negative only if it has minus)
It's because parseInt takes a signed int and toHexString produces an unsigned result (see my answer)...
@roni, what if hex is of String value like String Hex=Integer.toHexString("xyz"); how to get back string from the hex as "xyz"
30
  • int to Hex :

    Integer.toHexString(intValue);
    
  • Hex to int :

    Integer.valueOf(hexString, 16).intValue();
    

You may also want to use long instead of int (if the value does not fit the int bounds):

  • Hex to long:

    Long.valueOf(hexString, 16).longValue()
    
  • long to Hex

    Long.toHexString(longValue)
    

Comments

11

It's worth mentioning that Java 8 has the methods Integer.parseUnsignedInt and Long.parseUnsignedLong that does what you wanted, specifically:

Integer.parseUnsignedInt("ffff8000",16) == -32768

The name is a bit confusing, as it parses a signed integer from a hex string, but it does the work.

Comments

7

Try using BigInteger class, it works.

int Val=-32768;
String Hex=Integer.toHexString(Val);

//int FirstAttempt=Integer.parseInt(Hex,16); // Error "Invalid Int"
//int SecondAttempt=Integer.decode("0x"+Hex);  // Error "Invalid Int"
BigInteger i = new BigInteger(Hex,16);
System.out.println(i.intValue());

Comments

6

As Integer.toHexString(byte/integer) is not working when you are trying to convert signed bytes like UTF-16 decoded characters you have to use:

Integer.toString(byte/integer, 16);

or

String.format("%02X", byte/integer);

reverse you can use

Integer.parseInt(hexString, 16);

Comments

3

Java's parseInt method is actally a bunch of code eating "false" hex : if you want to translate -32768, you should convert the absolute value into hex, then prepend the string with '-'.

There is a sample of Integer.java file :

public static int parseInt(String s, int radix)

The description is quite explicit :

* Parses the string argument as a signed integer in the radix 
* specified by the second argument. The characters in the string 
...
...
* parseInt("0", 10) returns 0
* parseInt("473", 10) returns 473
* parseInt("-0", 10) returns 0
* parseInt("-FF", 16) returns -255

Comments

2

Using Integer.toHexString(...) is a good answer. But personally prefer to use String.format(...).

Try this sample as a test.

byte[] values = new byte[64];
Arrays.fill(values, (byte)8);  //Fills array with 8 just for test
String valuesStr = "";
for(int i = 0; i < values.length; i++)
    valuesStr += String.format("0x%02x", values[i] & 0xff) + " ";
valuesStr.trim();

Comments

2

Below code would work:

int a=-32768;
String a1=Integer.toHexString(a);
int parsedResult=(int)Long.parseLong(a1,16);
System.out.println("Parsed Value is " +parsedResult);

Comments

1

Hehe, curious. I think this is an "intentianal bug", so to speak.

The underlying reason is how the Integer class is written. Basically, parseInt is "optimized" for positive numbers. When it parses the string, it builds the result cumulatively, but negated. Then it flips the sign of the end-result.

Example:

66 = 0x42

parsed like:

4*(-1) = -4
-4 * 16 = -64 (hex 4 parsed)

-64 - 2 = -66 (hex 2 parsed)

return -66 * (-1) = 66

Now, let's look at your example FFFF8000

16*(-1) = -16 (first F parsed)
-16*16 = -256 

-256 - 16 = -272 (second F parsed)
-272 * 16 = -4352 

-4352 - 16 = -4368 (third F parsed)
-4352 * 16 = -69888

-69888 - 16 = -69904 (forth F parsed)
-69904 * 16 = -1118464 

-1118464 - 8 = -1118472 (8 parsed)
-1118464 * 16 = -17895552 

-17895552 - 0 = -17895552 (first 0 parsed)
Here it blows up since -17895552 < -Integer.MAX_VALUE / 16 (-134217728). 
Attempting to execute the next logical step in the chain (-17895552 * 16)
would cause an integer overflow error.

Edit (addition): in order for the parseInt() to work "consistently" for -Integer.MAX_VALUE <= n <= Integer.MAX_VALUE, they would have had to implement logic to "rotate" when reaching -Integer.MAX_VALUE in the cumulative result, starting over at the max-end of the integer range and continuing downwards from there. Why they did not do this, one would have to ask Josh Bloch or whoever implemented it in the first place. It might just be an optimization.

However,

Hex=Integer.toHexString(Integer.MAX_VALUE);
System.out.println(Hex);
System.out.println(Integer.parseInt(Hex.toUpperCase(), 16));

works just fine, for just this reason. In the sourcee for Integer you can find this comment.

// Accumulating negatively avoids surprises near MAX_VALUE

1 Comment

// Accumulating negatively avoids surprises near MAX_VALUE -> but it introduces surprises lower 0 ^^
0
public String toHex(int num) {
    if (num == 0)
        return "0";

    char[] hexChars = "0123456789abcdef".toCharArray();
    StringBuilder hex = new StringBuilder();

    while (num != 0 && hex.length() < 8) {
        hex.insert(0, hexChars[num & 15]);
        num >>>= 4;
    }

    return hex.toString();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.