I don't understand why there's a difference between this code:
byte b = (byte) (0xff >> 1);
(so now b = 01111111),
and this code:
byte b = (byte) 0xff;
b >>= 1;
(but now b = 11111111).
Thanks in advance for your help!
In the first code, (0xff >> 1) is 255 >> 1, which is 127. That is calculated with ints and then you cast it to a byte. 127 as a byte is 01111111 bin.
In the second code, you start with (byte) 0xff, which is 11111111 bin, which is the two's complement representation of -1 in 8 bits. So (byte) 0xff is -1.
When you perform shifting, the byte value -1 is promoted to the int value -1. That's 11111111 11111111 11111111 11111111 bin.
Shifting it right one place with the arithmetic right shift operator, (-1) >> 1 gives you 11111111 11111111 11111111 11111111 again, because the >> operator on a negative number moves the bits to the right and fills in the left with ones instead of zeroes.
Then, since you're using >>=, the result is cast back to a byte to be stored in b. That only retains the last 8 bits, which are 11111111.
Alternatively, if you used the logical right shift operator, (-1) >>> 1 would give you 01111111 11111111 11111111 11111111 in binary (a zero followed by 31 ones). Since the last 8 bits are the same, this would still give you 11111111 when it is cast back to a byte.
Related
I'm coding a personal project in Java right now and have recently been using bit operations for the first time. I was trying to convert two bytes into a short, with one byte being the upper 8 bits and the other being the lower 8 bits.
I ran into an error when running the first line of code below.
Incorrect Results
short regPair = (short) ( (byte1 << 8) + (byte2) );
Correct Results
short regPair = (short) ( (byte1 << 8) + (byte2 & 0xFF) );
The expected results were: AAAAAAAABBBBBBBB, where A represents bits from byte1 and B represents bits from byte2.
Using the 1st line of code I would get the typical addition between a bit-shifted byte1 with byte 2 added to it.
Example of incorrect results
byte1 = 11, byte2 = -72
result = 2816 -72
= 2744
When using the line of code which produces the expected results I can get the proper answer of 3000. I am curious as to why the bit-masking is needed for byte2. My thoughts are that it converts byte2 into binary before the addition and then performs binary addition with both bytes.
In the incorrect case, byte2 is promoted to an int because of the + operator. This doesn't just mean adding some zeros to the start of the binary representation of byte2. Since integer types are represented in two's complement in Java, 1s will be added. After the promotion, byte2 becomes:
1111 1111 1111 1111 1111 1111 1011 1000
By doing & 0xFF, you force the promotion to int first, then you keep the least significant 8 bits: 1011 1000 and make everything else 0.
Print the intermediate value directly to see what is going on. Like,
System.out.printf("%d %s%n", ((byte) -72) & 0xFF, Integer.toBinaryString(((byte) -72) & 0xFF));
I get
184 10111000
So the correct code is actually adding 184 (not subtracting 72).
So I totally forgot that byte is singed in Java, therefore when performing math with a variable of this data type it will take the signed interpretation and not the direct value of the bits. By performing byte2 & 0xFF, Java converts the signed byte value into an unsigned int with all but the first 8 bits set as 0's. Therefore you can perform binary addition correctly.
signed byte value 0x11111111 = -1
unsigned byte value 0x11111111 = 255
In both cases byte values are promoted to int in the expression when
it is evaluated.
byte byte1 = 11, byte2 = -72;
short regPair = (short) ( (byte1 << 8) + (byte2) );
(2816) + (-72) = 2744
And even in below expression byte is promoted to int
short regPair = (short) ( (byte1 << 8) + (byte2 & 0xFF) );
2816 + 184 = 3000
Here in this expression there is no concatenation of two bytes like it has been expressed in the above question- AAAAAAAABBBBBBBB, where A represents bits from byte1 and B represents bits from byte2.
Actually -7 & 255 gives 184 which is added to 2816 to give the output 3000.
I have a sample code snippet like this:
byte a = -0b00001111;
byte b = a;
byte c = a;
System.out.println("a=" + a );
System.out.println("b=" + (b >> 1) );
System.out.println("c=" + (c >>> 1) );
and, it prints:
a=-15
b=-8
c=2147483640
I don't quite understand how b and c became those 2 values respectively, could someone demonstrate me in steps how those 2 values were calculated please?
For byte a, you have the literal 0b00001111, which is binary for 15, so a is -15. The bit representation of -15 for a byte is:
11110001
In Java, unary numeric promotion occurs on the operands of bit-shift operators <<, >>, and >>>.
Unary numeric promotion (§5.6.1) is performed on each operand separately.
This means that the value is promoted to int before shifting.
The code (b >> 1) promotes b to an int, then shifts the value with sign extension. This means that if the value was already negative, then a 1 bit is shifted to ensure it's still negative.
11110001
is promoted to
11111111 11111111 11111111 11110001
which is -15 as an int. After shifting to the right one bit, with sign extension:
11111111 11111111 11111111 11111000
which is -8.
However, for the code (c >>> 1), the >>> unsigned right shift operator does not perform sign extension, even if the promotion to int does maintain the sign and the value.
11110001
is promoted to
11111111 11111111 11111111 11110001
which is -15 as an int as before. After unsigned shifting to the right one bit:
01111111 11111111 11111111 11111000
The first bit is now 0. Without the most significant bit set, the value is now 231 - 8, or 2147483640.
I need to represent a value of 0xFF00 as two bytes (in Java). I am trying to do it like this:
int val = 0xFF00;
bytearray[0] = (byte)((val >> 8) & 0xFF);
bytearray[1] = (byte)((val >> 0) & 0xFF);
I know that byte in Java can hold values 0-255. So I expect the first array element to have a value of 255 and the second element to be zero. But what I am getting instead is -1 and 0. What I am doing wrong? What this -1 value mean?
Byte in java is from -128 to 127, not from 0 to 255
-1 is 1111 1111 in two's complement binary, equal to 255 in unsigned byte.
You aren't doing anything wrong, you just need to know that if you see -1, it means the byte is representing the bits 1111 1111.
Consider the following snip of java code
byte b=(byte) 0xf1;
byte c=(byte)(b>>4);
byte d=(byte) (b>>>4);
output:
c=0xff
d=0xff
expected output:
c=0x0f
how?
as b in binary 1111 0001
after unsigned right shift 0000 1111 hence 0x0f but why is it 0xff how?
The problem is that all arguments are first promoted to int before the shift operation takes place:
byte b = (byte) 0xf1;
b is signed, so its value is -15.
byte c = (byte) (b >> 4);
b is first sign-extended to the integer -15 = 0xfffffff1, then shifted right to 0xffffffff and truncated to 0xff by the cast to byte.
byte d = (byte) (b >>> 4);
b is first sign-extended to the integer -15 = 0xfffffff1, then shifted right to 0x0fffffff and truncated to 0xff by the cast to byte.
You can do (b & 0xff) >>> 4 to get the desired effect.
I'd guess that b is sign extended to int before shifting.
So this might work as expected:
(byte)((0x000000FF & b)>>4)
According to Bitwise and Bit Shift Operators:
The unsigned right shift operator ">>>" shifts a zero into the leftmost position, while the leftmost position after ">>" depends on sign extension.
So with b >> 4 you transform 1111 0001 to 1111 1111 (b is negative, so it appends 1) which is 0xff.
Java tries to skimp on having explicit support for unsigned basic types by defining the two different shift operators instead.
The question talks about unsigned right shift, but the examples does both (signed and unsigned), and shows the value of the signed shift (>>).
Your calculations would be right for unsigned shift (>>>).
The byte operand is promoted to an int before the shift.
See https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.19
Unary numeric promotion (§5.6.1) is performed on each operand separately. (Binary numeric promotion (§5.6.2) is not performed on the operands.)
And https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.1
Otherwise, 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).
byte b=(byte) 0xf1;
if (b<0)
d = (byte) ((byte) ((byte)(b>>1)&(byte)(0x7F)) >>>3);
else
d = (byte)(b>>>4);
First, check the value:
If the value is negative. Make one right shift, then & 0x7F, It will be changed to positive. then you can make the rest of right shift (4-1=3) easily.
If the value is positive, make all right shift with >>4 or >>>4. It does'nt make no difference in result nor any problem of right shift.
What is the difference between >>> and >> operators in Java?
>> is arithmetic shift right, >>> is logical shift right.
In an arithmetic shift, the sign bit is extended to preserve the signedness of the number.
For example: -2 represented in 8 bits would be 11111110 (because the most significant bit has negative weight). Shifting it right one bit using arithmetic shift would give you 11111111, or -1. Logical right shift, however, does not care that the value could possibly represent a signed number; it simply moves everything to the right and fills in from the left with 0s. Shifting our -2 right one bit using logical shift would give 01111111.
>>> is unsigned-shift; it'll insert 0. >> is signed, and will extend the sign bit.
JLS 15.19 Shift Operators
The shift operators include left shift <<, signed right shift >>, and unsigned right shift >>>.
The value of n>>s is n right-shifted s bit positions with sign-extension.
The value of n>>>s is n right-shifted s bit positions with zero-extension.
System.out.println(Integer.toBinaryString(-1));
// prints "11111111111111111111111111111111"
System.out.println(Integer.toBinaryString(-1 >> 16));
// prints "11111111111111111111111111111111"
System.out.println(Integer.toBinaryString(-1 >>> 16));
// prints "1111111111111111"
To make things more clear adding positive counterpart
System.out.println(Integer.toBinaryString(121));
// prints "1111001"
System.out.println(Integer.toBinaryString(121 >> 1));
// prints "111100"
System.out.println(Integer.toBinaryString(121 >>> 1));
// prints "111100"
Since it is positive both signed and unsigned shifts will add 0 to left most bit.
Related questions
Right Shift to Perform Divide by 2 On -1
Is shifting bits faster than multiplying and dividing in Java? .NET?
what is c/c++ equivalent way of doing ‘>>>’ as in java (unsigned right shift)
Negative logical shift
Java’s >> versus >>> Operator?
What is the difference between the Java operators >> and >>>?
Difference between >>> and >> operators
What’s the reason high-level languages like C#/Java mask the bit shift count operand?
1 >>> 32 == 1
>>> will always put a 0 in the left most bit, while >> will put a 1 or a 0 depending on what the sign of it is.
They are both right-shift, but >>> is unsigned
From the documentation:
The unsigned right shift operator ">>>" shifts a zero into the leftmost position, while the leftmost position after ">>" depends on sign extension.
The logical right shift (v >>> n) returns a value in which the bits in v have been shifted to the right by n bit positions, and 0's are shifted in from the left side. Consider shifting 8-bit values, written in binary:
01111111 >>> 2 = 00011111
10000000 >>> 2 = 00100000
If we interpret the bits as an unsigned nonnegative integer, the logical right shift has the effect of dividing the number by the corresponding power of 2. However, if the number is in two's-complement representation, logical right shift does not correctly divide negative numbers. For example, the second right shift above shifts 128 to 32 when the bits are interpreted as unsigned numbers. But it shifts -128 to 32 when, as is typical in Java, the bits are interpreted in two's complement.
Therefore, if you are shifting in order to divide by a power of two, you want the arithmetic right shift (v >> n). It returns a value in which the bits in v have been shifted to the right by n bit positions, and copies of the leftmost bit of v are shifted in from the left side:
01111111 >> 2 = 00011111
10000000 >> 2 = 11100000
When the bits are a number in two's-complement representation, arithmetic right shift has the effect of dividing by a power of two. This works because the leftmost bit is the sign bit. Dividing by a power of two must keep the sign the same.
Read more about Bitwise and Bit Shift Operators
>> Signed right shift
>>> Unsigned right shift
The bit pattern is given by the left-hand operand, and the number of positions to shift by the right-hand operand. The unsigned right shift operator >>> shifts a zero into the leftmost position,
while the leftmost position after >> depends on sign extension.
In simple words >>> always shifts a zero into the leftmost position whereas >> shifts based on sign of the number i.e. 1 for negative number and 0 for positive number.
For example try with negative as well as positive numbers.
int c = -153;
System.out.printf("%32s%n",Integer.toBinaryString(c >>= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c <<= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c >>>= 2));
System.out.println(Integer.toBinaryString(c <<= 2));
System.out.println();
c = 153;
System.out.printf("%32s%n",Integer.toBinaryString(c >>= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c <<= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c >>>= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c <<= 2));
output:
11111111111111111111111111011001
11111111111111111111111101100100
111111111111111111111111011001
11111111111111111111111101100100
100110
10011000
100110
10011000
The right shift logical operator (>>> N) shifts bits to the right by N positions, discarding the sign bit and padding the N left-most bits with 0's. For example:
-1 (in 32-bit): 11111111111111111111111111111111
after a >>> 1 operation becomes:
2147483647: 01111111111111111111111111111111
The right shift arithmetic operator (>> N) also shifts bits to the right by N positions, but preserves the sign bit and pads the N left-most bits with 1's. For example:
-2 (in 32-bit): 11111111111111111111111111111110
after a >> 1 operation becomes:
-1: 11111111111111111111111111111111
>> Signed right shift
>>> Unsigned right shift
Example:-
byte x, y; x=10; y=-10;
SOP("Bitwise Left Shift: x<<2 = "+(x<<2));
SOP("Bitwise Right Shift: x>>2 = "+(x>>2));
SOP("Bitwise Zero Fill Right Shift: x>>>2 = "+(x>>>2));
SOP("Bitwise Zero Fill Right Shift: y>>>2 = "+(y>>>2));
output would be :-
Bitwise Left Shift: x<<2 = 40
Bitwise Right Shift: x>>2 = 2
Bitwise Zero Fill Right Shift: x>>>2 = 2
Bitwise Zero Fill Right Shift: y>>>2 = 1073741821
>>(signed) will give u different result for 8 >> 2, -8 >> 2.
right shift of 8
8 = 1000 (In Binary)
perform 2 bit right shift
8 >> 2:
1000 >> 2 = 0010 (equivalent to 2)
right shift of -8
8 = 1000 (In Binary)
1's complement = 0111
2's complement:
0111 + 1 = 1000
Signed bit = 1
perform 2 bit right shift (on 2's co result)
8 >> 2:
1000 >> 2 = 1110 (equivalent to -2)
>>(unsigned) will give u same result for 8 >>> 2, -8 >>> 2.
unsigned right shift of 8
8 = 1000
8 >>> 2 = 0010
unsigned right shift of -8
-8 = 1000
-8 >>> 2 = 0010