3

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.

1
  • Your edit is a totally new question (very weird behavior, too). I suggest posting it as a separate question. The new question won't get any attention tacked onto the end of this question. (Also, I don't see why it should be behaving that way; the question needs broader attention.) Commented Aug 26, 2013 at 3:51

2 Answers 2

8

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)
Sign up to request clarification or add additional context in comments.

6 Comments

The second option is what you want in my opinion. The first only works because it widens the value to an int in the process. (The latter does too, but wouldn't need to). Not a big deal since you're using in an int context anyway, but I think the latter is more self-documenting.
Java also has >>>, which always shifts in a zero bit even on signed numbers.
@MarkPeters - Six of one, half a dozen of the other. Both work because of the rules of the language.
@LeeDanielCrocker - That's irrelevant to this problem. Using >>> would only get the high four bits to be 0. OP needs the high 28 bits to be 0.
@Ted: You're right, it's just a question of transparency. Normally when I do the former I use 0x0ff to make it abundantly clear. But it's more because I personally need reminding when it comes to signed bit operations.
|
4

java will bit extend when widening bytes to integer so you need to mask out the high order bits.

    System.out.println("First nibble: " + hex_val[(bytes[i] >> 4) & 0x0f]);
    System.out.println("Second nibble: " + hex_val[(bytes[i] & 0x0f)]);

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.