3

I have seen several questions on the topic mentioned in the subject (e.g this one), but it seems to me that none of them provided this example. I'm using Java7 and I want to convert a String representing an hexadecimal or a decimal into an Integer or Long value (depends on what it represents) and I do the following:

public Number getIntegerOrLong(String num) {
    try {
        return Integer.decode(num);
    } catch (NumberFormatException nf1) {
        final long decodedLong = Long.decode(num);
        if ((int) decodedLong == decodedLong) {//used in Java8 java.util.Math.toIntExact()
            return (int) decodedLong;
        }
        return decodedLong;
    }
}

When I use a String representing a decimal number everything is ok, the problem are arising with negative hexadecimals

Now, If I do:

String hex = "0x"+Integer.toHexString(Integer.MIN_VALUE);
Object number = getIntegerOrLong(hex);
assertTrue(number instanceof Integer):

fails, because it returns a Long. Same for other negative integer values.

Moreover, when I use Long.MIN_VALUE like in the following:

String hex = "0x"+Integer.toHexString(Long.MIN_VALUE);
Object number = getIntegerOrLong(hex);
assertTrue(number instanceof Long):

fails, because of NumberFormatException with message:

java.lang.NumberFormatException: For input string: "8000000000000000"

I also tried with other random Long values (so within the Long.MIN_VALUE and Long.MAX_VALUE, and it fails as well when I have negative numbers. E.g.

the String with the hexadecimal 0xc0f1a47ba0c04d89 for the Long number -4,543,669,698,155,229,815 returns:

java.lang.NumberFormatException: For input string: "c0f1a47ba0c04d89"

How can I fix the script to obtain the desired behavior?

Community
  • 1
  • 1
mat_boy
  • 12,998
  • 22
  • 72
  • 116
  • @Aaron, sorry, bad typing. I do `"0x"+Integer.toHexString(Integer.MIN_VALUE)`. Is this bad also? – mat_boy Oct 07 '16 at 10:55

1 Answers1

2

Long.decode and Integer.decode do not accept complemented values such as returned by Integer.toHexString : the sign should be represented as a leading - as described by the DecodableString grammars found in the javadoc.

The sequence of characters following an optional sign and/or radix specifier ("0x", "0X", "#", or leading zero) is parsed as by the Long.parseLong method with the indicated radix (10, 16, or 8). This sequence of characters must represent a positive value or a NumberFormatException will be thrown. The result is negated if first character of the specified String is the minus sign

If you can change the format of your input String, then produce it with Integer.toString(value, 16) rather than Integer.toHexString(value).

If you can switch to Java 8, use parseUnsignedInt/Long.

Aaron
  • 24,009
  • 2
  • 33
  • 57
  • I cannot return BigInteger for backward compatibility. For the cast, I made a mistake in the example that I typed, actually I was doing `"0x"+Integer.toHexString(Integer.MIN_VALUE)` and `"0x"+Long.toHexString(Long.MIN_VALUE)` – mat_boy Oct 07 '16 at 10:59
  • I'm not sure of what you are suggesting exactly. For instance, if you do `Long.parseUnsignedLong(Long.toString(Long.MIN_VALUE, 16), 16)` you still get a n exception, i.e. `java.lang.NumberFormatException: Illegal leading minus sign on unsigned string -8000000000000000` – mat_boy Oct 07 '16 at 11:29
  • I can confirm that `Long.parseUnsignedLong(Long.toHexString(Long.MIN_VALUE), 16)` works (as well as the `Integer` counterpart) – mat_boy Oct 07 '16 at 11:32
  • The leading sign should be conforming to the grammar described in [`Long/Int.decode`](https://docs.oracle.com/javase/7/docs/api/java/lang/Long.html#decode(java.lang.String)) : for an hex representation, it could be `Signopt 0x HexDigits`, i.e. `-0x8000000000000000` – Aaron Oct 07 '16 at 11:39