why Java throws a NumberFormatException - java

I got an exception while parsing a string to byte
String Str ="9B7D2C34A366BF890C730641E6CECF6F";
String [] st=Str.split("(?<=\\G.{2})");
byte[]bytes = new byte[st.length];
for (int i = 0; i <st.length; i++) {
bytes[i] = Byte.parseByte(st[i]);
}

That's because the default parse method expects a number in decimal format, to parse hexadecimal number, use this parse:
Byte.parseByte(st[i], 16);
Where 16 is the base for the parsing.
As for your comment, you are right. The maximum value of Byte is 0x7F. So you can parse it as int and perform binary AND operation with 0xff to get the LSB, which is your byte:
bytes[i] = Integer.parseInt(st[i], 16) & 0xFF;

Assuming you want to parse the string as hexadecimal, try this:
bytes[i] = Byte.parseByte(st[i], 16);
The default radix is 10, and obviously B is not a base-10-digit.

Java is very picky on signedness, it will not accept values to overflow. Thus, if you parse a Byte and it is larger than 127 (for example, 130 dec or 83 hex) you will get a NumberFormatException. Same happens if you parse an 8 digit hex number as an Integer (or a 16 digit hex number as a Long) and it starts with 8-F. Such values will not be interpreted as negative (two's complement) but as illegal.
If you think that this is anal retentive, I totally agree. But that's Java style.
To parse hex values as two's complement numbers either use a large enough integer type (for example, if you are parsing a Byte use Integer instead and type cast it to a byte later) or -- if you need to parse a Long, split the number in half it is 16 digits, then combine. Here's an example:
public static long longFromHex(String s) throws IllegalArgumentException {
if (s.length() == 16)
return (Long.parseLong(s.substring(0,8),16)<<32)|(Long.parseLong(s.substring(8,16),16)&0xffffffffL);
return Long.parseLong(s, 16);
}
Or, to read a Byte, just use Integer instead:
public static byte byteFromHex(String s) throws IllegalArgumentException {
int i = Integer.parseInt(s, 16);
if (i < 0 || i > 255) throw new IllegalArgumentException("input string "+s+" does not fit into a Byte");
return (byte)i;
}

Related

Convert hex string value to hex int

How can I convert the hexstring into the form of hexint below?
string hexstring = "0x67";
int hexint = 0x67;
Integer#decode can be used to convert hexadecmial string representation into its integer value:
Integer.decode("0x67");
This function automatically detects the correct base and will parse return the int 103 (0x67 = 6*16+7). If you want to manually specify a different base, see my other answer
If you only have a single byte, you can strip of the leading "0x" part and then parse as a base-16 number with Integer#parseInt:
Integer.parseInt("0x67".substring(2), 0x10);
Integer.parseInt("0x67".substring(2), 16);
0x10 is the hexadecimal representation of the decimal number 16.
String hexstring = "67";
int hexint = Integer.parseInt(hexstring, 16);
System.out.println(hexint); // 103 (decimal)
With Integer.parseInt(String s, int radix) you can parse a String to an int.
The radix is the base of that number system, in case of hex-values it is 16.

Signed Hexadecimal to decimal in Java

I was wondering if it's possible to convert a signed Hexadecimal (negative) to its corresponding decimal value.
I assume that you have a hexadecimal value in form of a String.
The method parseInt(String s, int radix) can take a hexadecimal (signed) String and with the proper radix (16) it will parse it to an Integer.
int decimalInt = parseInt(hexaStr, 16);
the solution above only works if you have numbers like -FFAA07BB... if you want the Two's complements you'll have to convert it yourself.
String hex = "F0BDC0";
// First convert the Hex-number into a binary number:
String bin = Integer.toString(Integer.parseInt(hex, 16), 2);
// Now create the complement (make 1's to 0's and vice versa)
String binCompl = bin.replace('0', 'X').replace('1', '0').replace('X', '1');
// Now parse it back to an integer, add 1 and make it negative:
int result = (Integer.parseInt(binCompl, 2) + 1) * -1;
or if you feel like having a one-liner:
int result = (Integer.parseInt(Integer.toString(Integer.parseInt("F0BDC0", 16), 2).replace('0', 'X').replace('1', '0').replace('X', '1'), 2) + 1) * -1;
If the numbers get so big (or small), that an Integer will have an overflow, use Long.toString(...) and Long.parseLong(...) instead.

Error Converting Hex to Float

I am trying to convert this Hex Value C2DE70A4 in Android. This is the Code that I am writing for Conversion:
try {
String hexString = "C2DE70A4";
float myFloat = Float.intBitsToFloat(Integer.parseInt(hexString, 16));
}
catch (Exception e) {
Toast.makeText(getApplicationContext(),e.getMessage(),Toast.LENGTH_LONG).show();
}
But it is causing an Exception and telling me that Invalid int: "C2DE70A4"
Can anyone help me to make the Conversion? The Float value should be: -111.22
Thanks.
0xC2DE70A4 in decimal is 3,269,357,732, but an int's max value is 2,147,483,647. That's why you can't parse it -- the parsed value is too big to fit in an int.
You should parse the string as a long, then cast it to an int , and finally call Float.intBitsToFloat on that:
long asLong = Long.parseLong(hexString, 16);
int asInt = (int) asLong;
float myFloat = Float.intBitsToFloat(asInt);
// or, just
float myFloat = Float.intBitsToFloat((int)Long.parseLong(hexString, 16));
Detailed explanation:
Fundamentally, the problem is that C2DE70A4 represents an unsigned value, whereas ints in Java are signed. If you parse it as a long and then look at its bits (via Long.toBinaryString), you'll see:
11000010110111100111000010100100
That's a 32-length string, because toBinaryString omits leading 0s. So why didn't it fit in a 32-bit int? Because that binary string above represents an unsigned number. Or, more precisely, it's really shorthand for this signed, 64-bit, two's complement number:
0000000000000000000000000000000011000010110111100111000010100100
... which equals 3269357732 in decimal, which is out of the range of an int because an int is signed, meaning that the leftmost digit is the sign and not part of the number's magnitude (aka how "big" it is).
If you take that long and cast it to an int, it'll discard the leftmost 32 digits, leaving you with a 32-bit int 11000010110111100111000010100100 -- which corresponds to -1025609564 in decimal (again, in two's complement). If you then take that number and feed it to intBitsToFloat, you'll get -111.22.
change the hex string to 0xC2DE70A4..
public class Test {
public static void main (String[] args) {
String myString = "BF800000";
Long i = Long.parseLong(myString, 16);
Float f = Float.intBitsToFloat(i.intValue());
System.out.println(f);
System.out.println(Integer.toHexString(Float.floatToIntBits(f)));
}
}

Hash a String into fixed bit hash value

I want to hash a word into fixed bit hash value say 64 bit,32 bit (binary).
I used the following code
long murmur_hash= MurmurHash.hash64(word);
Then murmur_hash value is converted into binary by the following function
public static String intToBinary (int n, int numOfBits) {
String binary = "";
for(int i = 0; i < numOfBits; ++i) {
n/=2;
if(n%2 == 0)
{
binary="0"+binary;
}
else
binary="1"+binary;
}
return binary;
}
Is there any direct hash method to convert into binary?
Just use this
Integer.toBinaryString(int i)
If you want to convert into a fixed binary string, that is, always get a 64-character long string with zero padding, then you have a couple of options. If you have Apache's StringUtils, you can use:
StringUtils.leftPad( Long.toBinaryString(murmurHash), Long.SIZE, "0" );
If you don't, you can write a padding method yourself:
public static String paddedBinaryFromLong( long val ) {
StringBuilder sb = new StringBuilder( Long.toBinaryString(val));
char[] zeros = new char[Long.SIZE - sb.length()];
Arrays.fill(zeros, '0');
sb.insert(0, zeros);
return sb.toString();
}
This method starts by using the Long.toBinaryString(long) method, which conveniently does the bit conversion for you. The only thing it doesn't do is pad on the left if the value is shorter than 64 characters.
The next step is to create an array of 0 characters with the missing zeros needed to pad to the left.
Finally, we insert that array of zeros at the beginning of our StringBuilder, and we have a 64-character, zero-padded bit string.
Note: there is a difference between using Long.toBinaryString(long) and Long.toString(long,radix). The difference is in negative numbers. In the first, you'll get the full, two's complement value of the number. In the second, you'll get the number with a minus sign:
System.out.println(Long.toString(-15L,2));
result:
-1111
System.out.println(Long.toBinaryString(-15L));
result:
1111111111111111111111111111111111111111111111111111111111110001
Another other way is using
Integer.toString(i, radix)
you can get string representation of the first argument i in the radix ( Binary - 2, Octal - 8, Decimal - 10, Hex - 16) specified by the second argument.

Java converting int to hex and back again

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?
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.
It overflows, because the number is negative.
Try this and it will work:
int n = (int) Long.parseLong("ffff8000", 16);
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)
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.
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());
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);
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
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();
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);
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

Categories