Unsigned right shift in Java - java

Simple question: why if I apply unsigned right shift in Java to byte variable (and short as well) it threats it as int:
byte x = -1;
System.out.println(x >> 2);
System.out.println(x >>> 1);
System.out.println(Integer.MAX_VALUE);
Console output:
-1
2147483647
2147483647

One can only use the shift operators on ints and longs in Java (just like all other numeric operators), thus the byte is automatically cast to an int before shifting it. This also happens with the arithmetical right shift, but -1 >> 2 is -1 no matter what type -1 is, because the binary representation 111...111 shifted right arithmetically is still 111...111, while shifted logically it becomes 011...111, i.e. the maximum value of the shifted type.
PS: An arithmetic shift is a signed shift, and a logical shift is an unsigned shift.

Related

Java's unsigned bitshift right (>>>) is signed shifting

I'm trying to bitshift right in order to isolate certain bits from a byte so I wanted an unsigned shift, the "new" bits should be zeros by my understanding. However, I've found that >>> has no difference to >> in my tests. -128 >> 4 = -8 as expected, but -128 >>> 4 should be 8 but I still get -8.
byte data = (byte)-128;
System.out.println((byte)(data >>> 4));
System.out.println((byte)(data >> 4));
Thanks for the help.
The unsigned right-shift operator is indeed doing an unsigned right-shift in this code; it's just hidden because of implicit cast from byte to int. Says the Java Language Specification (§15.19):
The operators << (left shift), >> (signed right shift), and >>> (unsigned right shift) are called the shift operators. The left-hand operand of a shift operator is the value to be shifted; the right-hand operand specifies the shift distance. [...] Unary numeric promotion (§5.6.1) is performed on each operand separately.
Unary numeric promotion means (§5.6.1):
[...] if the operand is of compile-time type byte, short, or char, it is promoted to a value of type int by a widening primitive conversion (§5.1.2).
So your code is evaluated as follows:
The byte value -128 which is the left operand of >>> is promoted to the int value -128, which is 0b11111111111111111111111110000000 in binary.
The unsigned right-shift is done on this int value, with the result 268435448 which in binary is 0b00001111111111111111111111111000. Note that the four left-most bits are zero, as you would expect from an unsigned right-shift.
This result is then explicitly casted to (byte), giving the result -8.
Using the REPL:
> byte b = -128;
> int shifted = b >>> 4;
> shifted
268435448
> (byte) shifted
-8
For the behaviour you want, you can use & 0xFF to do an "unsigned" conversion from byte to int:
> ((b & 0xFF) >>> 4)
8
The signed right shift operator '>>' uses the sign bit to fill the trailing positions and the unsigned right shift operator '>>' do not use the sign bit to fill the trailing positions. It always fills the trailing positions by 0s

Moving bits to left

byte right =0xf; // 00001111
System.out.println(Integer.toBinaryString(right)); // 00001111
right = (byte)(right << 4);
System.out.println(Integer.toBinaryString(right)); // 11111111111111111111111111110000
why right is equal to 11111111111111111111111111110000? I explicitly cast it to byte = 8 bits.
I'm excpecting for an output: 11110000.
If you hadn't cast to byte, all would be well.
There are no shift operators defined for byte, so the value is first promoted to int - which is fine, and still 0000[...]1111.
You then shift left 4 bits, giving 0000[...]111000.
You then cast to byte, giving a byte of 11110000.
You then call Integer.toBinaryString, which takes an int parameter. That means the byte 1111000 is convert to int - at which point it's sign-extended to 11111111111111111111111111110000 (as per JLS 5.1.2), which is then printed.
Options:
Don't cast down to byte, so you don't end up with a conversion back to int. (e.g. use int shifted = right << 4; and then pass shifted into Integer.toBinaryString.)
Instead of leaving the conversion implicit, use right & 0xff to apply a bit-mask so you can only have the bottom 8 bits set.
Either of those prints 11110000 instead.

And bitwise operation gets negative value

I have this code:
int code = 0x92011202;
int a = (code & 0xF0000000) >> 28;
int b = (code & 0x0F000000) >> 24;
// ..
int n = (code & 0x0000000F);
But if most significant bit of code is equal to 1 (from 9 to F) a comes negative value. All other variables
works fine.
Why this happen?
This is explained in The Java Tutorials.
Specifically :
The unsigned right shift operator ">>>" shifts a zero into the
leftmost position, while the leftmost position after ">>" depends on
sign extension.
Java uses 2s complement variables. The only aspect about 2s complements that you care about is that, if the leftmost bit is a 1, the number is negative. The signed bitshift maintains sign, so if code is negative to begin with, it stays negative after the shift.
To fix your program use >>> instead which is a logical bitshift, ignoring sign
The most significant bit of code represents the sign -- 0 means the number is positive and 1 means the number is negative.
If you just print out code you'll find that it's negative.
Because the shift operator takes into account the sign (it's a signed shift), a will get a negative value if code is negative.
The max value of "int" is 2^31-1. 0xF0000000 is a negative number. And any number with most significant bit equals to 1 is negative .

Logical right shift operator in java

I am beginner to java... I have tried very much but could not find the way the following line
System.out.println (-1>>>1);
gives 2147483647 ?
Can anyone help me ?
This is because the binary representation of -1 is 11111111111111111111111111111111. When you perform an unsigned right bit-shift operation (>>>) on it it moves all of the bits right by the argument (1 in this case) and fills in empty spaces on the left with zeros so you get 01111111111111111111111111111111 which is the binary representation of Integer.MAX_VALUE = 2147483647 (not sure where you got 2147483648 from).
>>> is the bitwise right-shift operator, with 0 sign extension - in other words, all bits "incoming" from the left are filled with 0s.
-1 is represented by 32 bits which are all 1. When you shift that right by 1 bit with 0 sign extension, you end up with a value which has the 31 bottom bits still 1, but 0 for the top bit (the sign bit), so you end up with Integer.MAX_VALUE - which is 2147483647, not 2147483648 as your post states.
Or in JLS terms, from section 15.19:
The value of n >>> s is n right-shifted s bit positions with zero-extension, where:
If n is positive, then the result is the same as that of n >> s.
If n is negative and the type of the left-hand operand is int, then the result is equal to that of the expression (n >> s) + (2 << ~s).
If n is negative and the type of the left-hand operand is long, then the result is equal to that of the expression (n >> s) + (2L << ~s).
This definition ends up being a bit of a pain to work with - it's easier to just work with the "0 sign extension right-shift" explanation, IMO.

Reading bytes in Java

I am trying to understand how the following line of code works:
for (int i = 0; i < numSamples; i++) {
short ampValue = 0;
for (int byteNo = 0; byteNo < 2; byteNo++) {
ampValue |= (short) ((data[pointer++] & 0xFF) << (byteNo * 8));
}
amplitudes[i] = ampValue;
}
As far as I understand, this is reading 2 bytes (as 2 bytes per sample) in a inclusive manner, i.e. the ampValue is composed of two byte reads. The data is the actual data sample (file) and the pointer is increasing to read it upto the last sample. But I don't understand this part:
"data[pointer++] & 0xFF) << (byteNo * 8)); "
Also, I am wondering whether it makes any difference if I want to read this as a double instead of short?
Looks like data[] is the array of bytes.
data[pointer++] gives you a byte value in the range [-128..127].
0xFF is an int contstant, so...
data[pointer++] & 0xFF promotes the byte value to an int value in the range [-128..127]. Then the & operator zeroes out all of the bits that are not set in 0xFF (i.e., it zeroes out the 24 upper bits, leaving only the low 8 bits.
The value of that expression now will be in the range [0..255].
The << operator shifts the result to the left by (byteNo * 8) bits. That's the same as saying, it multiplies the value by 2 raised to the power of (byteNo * 8). When byteNo==0, it will multiply by 2 to the power 0 (i.e., it will multiply by 1). When byteNo==1, it will multiply by 2 to the power 8 (i.e., it will multiply by 256).
This loop is creating an int in the range [0..65535] (16 bits) from each pair of bytes in the array, taking the first member of each pair as the low-order byte and the second member as the high-order byte.
It won't work to declare ampValue as double, because the |= operator will not work on a double, but you can declare the amplitudes[] array to be an array of double, and the assignment amplitudes[i] = ampValue will implicitly promote the value to a double value in the range [0.0..65535.0].
Additional info: Don't overlook #KevinKrumwiede's comment about a bug in the example.
In Java, all bytes are signed. The expression (data[pointer++] & 0xFF) converts the signed byte value to an int with the value of the byte if it were unsigned. Then the expression << (byteNo * 8) left-shifts the resulting value by zero or eight bits depending on the value of byteNo. The value of the whole expression is assigned with bitwise or to ampValue.
There appears to be a bug in this code. The value of ampValue is not reset to zero between iterations. And amplitude is not used. Are those identifiers supposed to be the same?
Let's break down the statement:
|= is the bitwise or and assignment operator. a |= b is equivalent to a = a | b.
(short) casts the int element from the data array to a short.
pointer++ is a post-increment operation. The value of pointer will be returned and used and then immediately incremented every single time it's accessed in this fashion - this is beneficial in this case because the outer-loop is cycling through 2-byte samples (via the inner loop) from the contiguous data buffer, so this keeps incrementing.
& is the bitwise AND operator and 0xFF is the hexadecimal value for the byte 0b11111111 (255 in decimal); the expression data[pointer++] & 0xFF is basically saying, for each bit in the byte retrieved from the data array, AND it with 1. In this context, it forces Java, which by default stores signed byte objects (i.e. values from -128 to 127 in decimal), to return the value as an unsigned byte (i.e. values from 0 to 255 decimal).
Since your samples are 2 bytes long, you need to shift the second lot of 8 bits left, as the most significant bits, using the left bit-shift operator <<. The byteNo * 8 ensures that you're only shifting bits when it's the second of the two bytes.
After the two bytes have been read, ampValue will now contain the value of the sample as a short.

Categories

Resources