Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anding with 0xff, clarification needed

In the following snippet consider replacing line 8 with commented equivalent

1. private static String ipToText(byte[] ip) {
2.  StringBuffer result = new StringBuffer();
3.
4.  for (int i = 0; i < ip.length; i++) {
5.      if (i > 0)
6.          result.append(".");
7.
8.      result.append(ip[i]); // compare with result.append(0xff & ip[i]);
9.  }
10.
11.     return result.toString();
12. }

.equals() test confirms that adding 0xff does not change anything. Is there a reason for this mask to be applied?

like image 409
James Raitsev Avatar asked Mar 30 '12 20:03

James Raitsev


3 Answers

byte in Java is a number between −128 and 127 (signed, like every integer in Java (except for char if you want to count it)). By anding with 0xff you're forcing it to be a positive int between 0 and 255.

It works because Java will perform a widening conversion to int, using sign extension, so instead of a negative byte you will have a negative int. Masking with 0xff will leave only the lower 8 bits, thus making the number positive again (and what you initially intended).

You probably didn't notice the difference because you tested with a byte[] with only values smaller than 128.

Small example:

public class A {
    public static void main(String[] args) {
        int[] ip = new int[] {192, 168, 101, 23};
        byte[] ipb = new byte[4];
        for (int i =0; i < 4; i++) {
            ipb[i] = (byte)ip[i];
        }

        for (int i =0; i < 4; i++) {
            System.out.println("Byte: " + ipb[i] + ", And: " + (0xff & ipb[i]));
        }
    }
}

This prints

Byte: -64, And: 192
Byte: -88, And: 168
Byte: 101, And: 101
Byte: 23, And: 23

showing the difference between what's in the byte, what went into the byte when it still was an int and what the result of the & operation is.

like image 94
Joey Avatar answered Sep 23 '22 12:09

Joey


As you're already working with an array of bytes here, and you're doing a bitwise operation, you can ignore how Java treats all bytes as signed. After all, you're working on the bit level now, and there is no such thing as "signed" or "unsigned" values on the level of bits.

Masking an 8-bit value (a byte) with all 1's is just a waste of cycles, as nothing will ever be masked off. A bitewise AND will return a bit true if both bits being compared are true, thus if the mask contains all 1's, then you're guaranteed that all bits of the masked value will remain unchanged after the AND operation.

Consider the following examples:

Mask off the upper nibble:
    0110 1010
AND 0000 1111 (0x0F)
  = 0000 1010
Mask off the lower nibble:
    0110 1010
AND 1111 0000 (0xF0)
  = 0110 0000
Mask off... Eh, nothing:
    0110 1010
AND 1111 1111 (0xFF)
  = 0110 1010

Of course, if you were working with a full blown int here, you'd get the result at the others have said: You'd "force" the int to be the equivalent of an unsigned byte.

like image 31
Wkter Avatar answered Sep 25 '22 12:09

Wkter


In this example, I don't see how it would make any difference. You are anding the 0xff with a byte. A byte by definition has 8 bits, and the add masks off the last 8 bits. So you're taking the last 8 of 8, that's not going to do anything.

Anding with 0xff would make sense if the thing you were anding with it was bigger than a byte, a short or an int or whatever.

like image 43
Jay Avatar answered Sep 21 '22 12:09

Jay