Java defines several bitwise operators that can be applied to the integer types: long, int, short, char, and byte. These operators act upon the individual bits of their operands.
List of bitwise operators:
Operator | Result |
---|---|
~ | Bitwise unary NOT |
& | Bitwise AND |
| | Bitwise OR |
^ | Bitwise exclusive OR |
>> | Shift right |
>>> | Shift right zero fill |
<< | Shift left |
&= | Bitwise AND assignment |
|= | Bitwise OR assignment |
^= | Bitwise exclusive OR assignment |
>>= | Shift right assignment |
>>>= | Shift right zero fill assignment |
<<= | Shift left assignment |
Bitwise operator operates on bits of an integer. Java stores all integers types in two’s complement which means that negative numbers are represented by inverting (changing 1’s to 0’s and vice versa) all of the bits in a value, then adding 1 to the result.
Example:
-42 is represented by inverting all of the bits in 42, or 00101010, which yields 11010101, then adding 1, which results in 11010110, or -42.
To decode a negative number: first invert all of the bits, then add 1.
Example:
-42, or 11010110 inverted, yields 00101001, or 41, so when you add 1 you get 42.
Why Java uses two’s complement
The reason Java uses two’s complement is easy to see when you consider the issue of zero crossing.
Assuming the zero is represented as 00000000 in byte.In one’s complement, simply inverting all of the bits creates 11111111, which creates negative zero. The trouble is that negative zero is invalid in integer math. This problem is solved by using two’s complement to represent negative values. When using two’s complement, 1 is added to the complement, producing 100000000. This produces a 1 bit too far to the left to fit back into the byte value, resulting in the desired behavior, where –0 is the same as 0, and 11111111 is the encoding for –1. The same basic principle applies to all of Java’s integer types.
Java uses two’s complement to store negative numbers, applying the bitwise operators can easily produce unexpected results.
Example:
Turning on the high-order bit will cause the resulting value to be interpreted as a negative number, whether this is what you intended or not. To avoid unpleasant surprises, just remember that the high-order bit determines the sign of an integer no matter how that high-order bit gets set.
The Bitwise Logical Operators
The bitwise logical operators are &, |, ^, and ~. Bitwise operators are applied to each individual bit within each operand.
A | B | A | B | A & B | A ^ B | ~A |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 |
0 | 1 | 1 | 0 | 1 | 1 |
1 | 0 | 1 | 0 | 1 | 0 |
1 | 1 | 1 | 1 | 0 | 0 |
The Bitwise Not
The ~ is called unary NOT operator or bitwise complement, invert all the bits of its operand.
Example:
bit pattern of 42: 00101010 bit pattern of ~42: 11010101
The Bitwise AND
The & is called bitwise AND operator, produce 1 bit if both operands bit is 1. Zero is produced in all others cases.
Example:
00101010 42 & 00001111 15 ________ 00001010 10
The Bitwise OR
The | is called bitwise OR operator, produce 1 bit if either of the bits in the operands is 1.
Example:
00101010 42 | 00001111 15 ________ 00101111 47
The Bitwise XOR
The ^ is called bitwise XOR operator, produce 1 bit only if one operand is 1. In other case it produce 0 bit.
Example:
00101010 42 ^ 00001111 15 ________ 00100101 37
// Demonstrate the bitwise logical operators. public class BitwiseLogical { public static void main(String[] args) { String binary[] = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" }; int a = 3; // 0 + 2 + 1 or 0011 in binary int b = 6; // 4 + 2 + 0 or 0110 in binary int c = a | b; int d = a & b; int e = a ^ b; int f = (~a & b) | (a & ~b); int g = ~a & 0x0f; System.out.println(" a = " + binary[a]); System.out.println(" b = " + binary[b]); System.out.println(" a|b = " + binary[c]); System.out.println(" a&b = " + binary[d]); System.out.println(" a^b = " + binary[e]); System.out.println("~a&b|a&~b = " + binary[f]); System.out.println(" ~a&0x0f = " + binary[g]); } }
Output:
a = 0011 b = 0110 a|b = 0111 a&b = 0010 a^b = 0101 ~a&b|a&~b = 0101 ~a&0x0f = 1100
The Left Shift
The left shift operator, <<, shifts all of the bits in a value to the left a specified number of times. It has this general form:
Here, num specifies the number of positions to left-shift the value in value. For each shift left, the high-order bit is shifted out (and lost), and a zero is brought in on the right.
// Left shifting a byte value. class ByteLeftShift { public static void main(String args[]) { byte a = 64, b; int i; i = a << 2; b = (byte) (a << 2); System.out.println("Original value of a: " + a); System.out.println("i and b: " + i + " " + b); } }
Output:
Original value of a: 64 i and b: 256 0
When expression is evaluated all integers promoted to int. Since a is promoted to int for the purposes of evaluation, left-shifting the value 64 (0100 0000) twice results in i containing the value 256 (1 0000 0000).
If you left-shift a byte value, that value will first be promoted to int and then shifted. This means that you must discard the top three bytes of the result if what you want is the result of a shifted byte value.
The value in b contains 0 because after the shift, the low-order byte is now zero, discarded the highest three bytes from result.
The Right Shift
The right shift operator, >>, shifts all of the bits in a value to the right a specified number of times. Its general form is shown here:
Here, num specifies the number of positions to right-shift the value in value. When a value has bits that are “shifted off,” those bits are lost.
When you are shifting right, the top (leftmost) bits exposed by the right shift are filled in with the previous contents of the top bit. This is called sign extension and serves to preserve the sign of negative numbers when you shift them right. For example, –8 >> 1 is –4, which, in binary, is
11111000 -8 >> 1 11111100 -4
It is interesting to note that if you shift –1 right, the result always remains –1, since sign extension keeps bringing in more ones in the high-order bits.
// Right shifting a byte value. class ByteRightShift { public static void main(String args[]) { byte a = 64, b; int i; i = a >> 2; b = (byte) (a >> 2); System.out.println("Original value of a: " + a); System.out.println("i and b: " + i + " " + b); // negative value a = -64; i = a >> 2; b = (byte) (a >> 2); System.out.println("\nOriginal value of a: " + a); System.out.println("i and b: " + i + " " + b); } }
Output:
Original value of a: 64 i and b: 16 16 Original value of a: -64 i and b: -16 -16
The Unsigned Right Shift
As you have just seen, the >> operator automatically fills the high-order bit with its previous contents each time a shift occurs. This preserves the sign of the value. In case of unsigned shift a zero into the high-order bit no matter what its initial value was.
Java’s unsigned, shift- right operator, >>>, which always shifts zeros into the high-order bit.
// demonstrate int unsigned right shift public class IntUnsignedShift { public static void main(String[] args) { int i = -1; int a = i >>> 25; System.out.println("Original value of i = " + i); System.out.println("Value of a = " + a); } }
Output:
Original value of i = -1 Value of a = 127
Bitwise Operator Compound Assignments
All of the binary bitwise operators have a compound form similar to that of the algebraic operators, which combines the assignment with the bitwise operation.
Following two statement are equivalent:
a = a >> 4; a >>= 4;
// demonstrate operator compound assignment class OperatorCompoundAssignment { public static void main(String args[]) { int a = 1; int b = 2; int c = 3; a |= 4; b >>= 1; c <<= 1; a ^= c; System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("c = " + c); } }
Output:
a = 3 b = 1 c = 6
No comments :
Post a Comment