Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java bit-shift results in negative number

Tags:

java

bit-shift

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.

like image 620
user1722919 Avatar asked Feb 21 '26 01:02

user1722919


1 Answers

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)
like image 132
Ted Hopp Avatar answered Feb 23 '26 15:02

Ted Hopp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!