I am trying to make a method to use bit shifting to convert bytes to their hex (as a char) equivalent. However, I am experiencing some unexpected results: some of the numbers come back as negative. I understand that Java does not have the equivalent of unsigned integers, and I am at a loss as to how to make this work. Here is my code:
final static char[] hex_val = "0123456789ABCDEF".toCharArray();
public static void main(String[] args) {
byte[] bytes = {(byte) 0x58, (byte) 0x6D, (byte) 0x8F, (byte) 0xBA, (byte) 0xF5, (byte) 0x81};
for (int i = 0; i < bytes.length; i++) {
System.out.println("Run: " + i);
System.out.println("First nibble: " + hex_val[(bytes[i] >> 4)]);
System.out.println("Second nibble: " + hex_val[(bytes[i] & 0xf)]);
}
}
Here is the output:
Run: 0 First nibble: 5 Second nibble: 8 Run: 1 First nibble: 6 Second nibble: D Run: 2
Followed by: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -8 at Test.main(Test.java:10)
I know I can use String.format() to accomplish this, but I am not using that method because I need a method that works quickly while generating minimal garbage. My question for the experts is... what can I change to make this work?
UPDATE
I made the changes suggested by Ted Hopp, and it worked perfectly in the test method. I moved it over to my Android app, which converts the bytes to a MAC address into a char[] containing a formatted MAC. I'm no longer getting negative numbers, but I am getting what appear to be random miscalculations. Here is the method I am using:
static final char[] parser_hex_arr = "01234567890ABCDEF".toCharArray();
static final char[] parser_mac = " : : : : : ".toCharArray();
void parseMac() {
hex_sb.setLength(0);
for (hex_counter = 0; hex_counter < 6; hex_counter++) {
hex_sb.append(String.format("%02X", parser_packet_bytes[parser_skip + hex_counter]));
if (!(hex_counter == 5)) {
hex_sb.append(":");
}
}
parser_mac[0] = parser_hex_arr[ (parser_packet_bytes[parser_skip] >> 4) & 0x0f ];
parser_mac[1] = parser_hex_arr[ (parser_packet_bytes[parser_skip] & 0xf) ];
parser_mac[3] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 1] >> 4) & 0x0f ];
parser_mac[4] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 1] & 0xf) ];
parser_mac[6] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 2] >> 4) & 0x0f ];
parser_mac[7] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 2] & 0xf) ];
parser_mac[9] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 3] >> 4) & 0x0f ];
parser_mac[10] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 3] & 0xf) ];
parser_mac[12] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 4] >> 4) & 0x0f ];
parser_mac[13] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 4] & 0xf) ];
parser_mac[15] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 5] >> 4) & 0x0f ];
parser_mac[16] = parser_hex_arr[ (parser_packet_bytes[parser_skip + 5] & 0xf) ];
Log.i("PARSER", "StringBuilder.getString() = " + hex_sb.toString() + " | parser_mac = " + String.valueOf(parser_mac));
formatted_mac = String.valueOf(parser_mac);
}
parser_packet_bytes is a byte[] array of bytes of a packet, parser_skip is an int containing an offset where the byte is located, and hex_sb is a StringBuilder. The output from StringBuilder.toString() should be the same as String.valueOf(parser_mac)... but it isn't. Here is an example:
I/PARSER (10860): StringBuilder.getString() = AC:22:0B:40:70:41 | parser_mac = 0B:22:0A:40:70:41
I/PARSER (10860): StringBuilder.getString() = C8:F7:33:0E:7E:AF | parser_mac = B8:E7:33:0D:7D:0E
I/PARSER (10860): StringBuilder.getString() = 58:6D:8F:BA:F5:81 | parser_mac = 58:6C:8E:A0:E5:81
I/PARSER (10860): StringBuilder.getString() = AC:22:0B:40:70:41 | parser_mac = 0B:22:0A:40:70:41
My next question is... why don't they match? Thanks for any ideas that you may have.
This is due to sign extension. Byte values are signed in Java, so anything over 0x7f is treated as a negative value. The bit shift operators convert both operands to integers before doing the shift. You need to mask the bottom byte off after integer promotion. For example:
((bytes[i] & 0xff) >> 4)
or
((bytes[i] >> 4) & 0x0f)
instead of
(bytes[i] >> 4)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With