Java left shift clarification - java

I am unable to figure out the reason for this behavior. I want to left shift by 8 bits the byte value OxAB. Then I want to convert that to a long.
byte oneByte = (byte) 0xAB;
long converted = (oneByte & 0xFF) << 8;
System.out.println(converted);
In this case the output is 43776. However, if I change the code to this:
byte oneByte = (byte) 0xAB;
long converted = (oneByte) << 8;
System.out.println(converted);
The output changes to -21760. I have following questions:
What is the data type of 0xFF?
Why is bitwise AND with 0xFF preventing sign extension?

long converted = onebyte; // gives you -85
and if you shift it 8 times to the left it gives you exactly -21760
because during the conversion to long the left most bit is used as a sign bit.
long converted = onebyte & 0xFF; //gives you 171
and if you shift it 8 times to the left it gives you 43776
because when using the bitwise and byte, short and char are converted to an int first and then the bitwise operation is executed.
11111111111111111111111110101011 //byte 0xAB casted to int
00000000000000000000000011111111 //0xFF is an int literal
00000000000000000000000010101011 //bitwise and operation
after the bitwise and with 0xFF the sign bit is removed

Related

Why does 11010100 << 1 equal 110101000, not 10101000?

Why when I try to shift bits for 110101002, the result is 1101010002, not 101010002.
int a = Integer.parseInt("11010100", 2) << 1;
I try to do this:
int a = (byte)(Integer.parseInt("11010100", 2) << 1);
But if the output value is greater than 128, everything goes into minus, which is logical.
How can I make that number of bits does not change?
Let's take it one step at a time.
Integer.parseInt("11010100", 2) - this is the int value 212. This is, by the way, needless; you can just write: 0b11010100.
0b11010100 << 1 is the same as 0b110101000, and is 424.
You then cast it to a byte: (byte)(0b11010100 << 1). The bits beyond the first 8 all get lopped off, which leaves 0b10101000, which is -88. Minus, yes, because in java bytes are signed.
You then silently cast this -88 back up to int, as you assign it to an int value. It remains -88, which means all the top bits are all 1s.
Hence, the final value is -88.
If you want to see 168 instead (which is the exact same bits, but shown unsigned instead of signed), the usual trick is to use & 0xFF, which sets all bits except the first 8 to 0, thus guaranteeing a positive number:
byte b = (byte) (0b11010100 << 1);
System.out.println(b); // -88. It is not possible to print 168 when printing a byte.
int asUnsigned = b & 0xFF;
System.out.println(asUnsigned); // 168.
// or in one go:
System.out.println(((byte) (0b11010100 << 1)) & 0xFF); // 168
If you want to set to 0 all bits higher than the bottom 8 bits, you can use bit-wise AND:
int a = (Integer.parseInt("11010100", 2) << 1) & 0xff;
System.out.println (Integer.toString(a,2));
Output:
10101000
Try something like this:
int anInt = Integer.parseInt("11010100", 2) << 1;
int asUnsignedInt= Byte.toUnsignedInt((byte) anInt);
toUnsignedInt has been introduced in Java SE 8.

Reading Little Endian – LS Byte first for integer how to ignore the extra 0

I have been reading these byte by bytes from streams. Example I read this line like this.
int payloadLength = r.readUnsignedShort();
The problem I have is that 2 bytes value is x3100 so it turns out to be 12544 but I suppose to only read as x31 which makes it to be only 49. How to ignore the extra 00.
Right shift the value by 8 bits and then and it with 0xFF. Right shifting moves the bits 8 bits to the right. Any other bits would also be moved to the right so you need to mask those of by do an ANDing (&) with 0xFF to get rid of them.
int payloadLength = r.readUnsignedShort();
payloadLength = (payloadLength >>> 8)& 0xFF;
System.out.println(payLoadLength);
You may also want to swap the two bytes.
v = 0xa0b;
v = swapBytes(v);
System.out.println(Integer.toHexString(v)); // 0xb0a
public static int swapBytes(int v) {
return ((v << 8)&0xFF00) | ((v >> 8) & 0xFF);
}
Normally, for reading in just 16 bits you would not have to and it with 0xFF since the high order bits are 0's. But I think it is a good practice and will prevent possible problems in the future.

Issues when converting bytes to integers (Java specific?)

I'm a bit confused regarding a conversion from bytes to integers. Consider the following code:
byte[] data = new byte[] { 0, (byte) 0xF0 };
int masked = data[0] << 8 & 0xFF | data[1] & 0xFF; //240
int notMasked = data[0] << 8 | data[1]; //-16
Because bytes in java are signed, data[1] is not 240 decimal, but rather the 2's complement, -16. However, it should still be, in binary: 0x11110000 so, why do I need to do data[1] & 0xFF ?
Is Java converting everything to Integer before passing it to the | operator? Why does &0xFF make a difference then?
Java bytes are signed (unfortunately) - so when you promote the value to an int in order to perform the bitwise |, it ends up being sign-extended as 0xFFFFFFF0. That then messes up the | with data[0]. The masking with & 0xff converts it to an integer value of 240 (just 0x000000F0) instead.
However, you've stlil got a problem. This code:
int masked = data[0] << 8 & 0xFF | data[1] & 0xFF;
should be:
int masked = ((data[0] & 0xff) << 8) | (data[1] & 0xFF);
... otherwise you're masking after the shift, which won't work. I've added brackets because I'm never sure of the predence of &, << and |...
It is similar to a known "puzzle"
byte x = -1;
x = x >>>= 1;
System.out.println(x);
produces
-1
No shift? This is because before compiling arithemtic / shift / comparison expressions javac promotes byte (as well as short and char) to int or to long (if there is any long in the expression), so it works as follows
x -> int = 0xFFFFFFFF; 0xFFFFFFF >>> 1 = 0x7FFFFFF; (byte)0x7FFFFFF -> 0xFF

How does the call to the hashing algorithm work, particularly the use of bit shifting 0xff?

The following code snippet, sourced from Core Java Vol 2 (7th Ed), shows how to create an SHA1 and an MD5 fingerprint using Java.
It turns out that the only function that works is when I load the cleartext from a textfile.
How does MessageDigestFrame.computeDigest() work out the fingerprint, and, particularly the use of the bit shifting pattern (Line 171 - 172)?
public void computeDigest(byte[] b)
{
currentAlgorithm.reset();
currentAlgorithm.update(b);
byte[] hash = currentAlgorithm.digest();
String d = "";
for (int i = 0; i < hash.length; i++)
{
int v = hash[i] & 0xFF;
if (v < 16) d += "0";
d += Integer.toString(v, 16).toUpperCase() + " ";
}
digest.setText(d);
}
The method should work fine whatever you give it - if you're getting the wrong results, I suspect you're loading the file incorrectly. Please show that code, and we can help you work out what's going wrong.
In terms of the code, this line:
int v = hash[i] & 0xFF;
is basically used to treat a byte as unsigned. Bytes are signed in Java - an acknowledged design mistake in the language - but we want to print out the hex value as if it were an unsigned integer. The bitwise AND with just the bottom 8 bits effectively converts it to the integer value of the byte treated as unsigned.
(There are better ways to convert a byte array to a hex string, but that's a separate matter.)
It is not bit shifting, it is bit masking. hash[i] is a byte. When it is widened to integer you need to mask off the higher integer bits because of possible sign extension.
byte b = (byte)0xEF;
System.out.println("No masking: " + (int)b);
System.out.println("Masking: " + (int)(b & 0xFF));
This snipped:
int v = hash[i] & 0xFF;
if (v < 16) d += "0";
d += Integer.toString(v, 16).toUpperCase() + " ";
First you set all but the lowest 8 bits of v to 0 (because 0xFF is 11111111 in binary).
Then if the resulting number is only one digit in hex (< 16) you add a leading "0".
Finally convert the result to hex and add it to the string.

Java - strange errors with unsigned values

There is 2-bytes array:
private byte[] mData;
and method:
public void setWord(final short pData) {
mData[0] = (byte) (pData >>> 8);
mData[1] = (byte) (pData);
}
I wrote the simple test:
public void testWord() {
Word word = new Word();
word.setWord((short) 0x3FFF);
Assert.assertEquals(0x3F, word.getByte(0));
Assert.assertEquals(0xFF, word.getByte(1));
}
The second assert fails with message "Expected 255, but was -1".
I know, that 0xFF signed short is, in fact, -1, but why JUnit thinks, that they are not equal? And, what is the correct way to implement such classes?
Java does not support unsigned types, so in order for a value to be 255, it must not be a signed byte, which is incapable of holding the value of 255. The 0xFF constant value will be taken as a signed int, and for the comparison, the byte value 0xFF will be converted to an int at -1 as well.
You need to type cast the literal 0xFF to be a byte. Change the assert to be Assert.assertEquals((byte)0xFF, word.getByte(1)); Then the left hand side will evaluate to -1 as well as the right.
The comment from biziclop is correct.
Any Integer number you specify in your code is considered an Integer unless marked otherwise.
Change your assertion to:
Assert.assertEquals((byte)0xFF, word.getByte(1))
And it should pass fine - as the first two bytes of the integer will be considered as a
byte.
Bitwize speeking - basically when you write 0xFF the compiler interprets it as 0x000000FF which is 255.
You want 0xFFFFFFFF which is -1.
Casting to byte is the correct solution here
There are no unsigned types in java.
0xFF is the int 255 and casted to byte overflows to -1.
I usually work with bytes as integers if I want them unsigned. I usually do that this way:
int b1 = getByte() & 0xFF;
For example:
byte byte1 = 0xFF; // 255 = -1
byte byte2 = 0xFE; // 254 = -2
int int1 = (byte1 & 0xFF) + (byte1 & 0xFF); // 255 + 254 = 509

Categories

Resources