|
Shift operators
The shift operators also manipulate bits. They can be used solely with primitive, integral types. The left-shift operator (<<) produces the operand to the left of the operator shifted to the left by the number of bits specified after the operator (inserting zeroes at the lower-order bits). The signed right-shift operator (>>) produces the operand to the left of the operator shifted to the right by the number of bits specified after the operator. The signed right shift >> uses sign extension: if the value is positive, zeroes are inserted at the higher-order bits; if the value is negative, ones are inserted at the higher-order bits. Java has also added the unsigned right shift >>>, which uses zero extension: regardless of the sign, zeroes are inserted at the higher-order bits. This operator does not exist in C or C++.
If you shift a char, byte, or short, it will be promoted to int before the shift takes place, and the result will be an int. Only the five low-order bits of the right-hand side will be used. This prevents you from shifting more than the number of bits in an int. If you’re operating on a long, you’ll get a long result. Only the six low-order bits of the right-hand side will be used, so you can’t shift more than the number of bits in a long.
Shifts can be combined with the equal sign (<<= or >>= or >>>=). The lvalue is replaced by the lvalue shifted by the rvalue. There is a problem, however, with the unsigned right shift combined with assignment. If you use it with byte or short, you don’t get the correct results. Instead, these are promoted to int and right shifted, but then truncated as they are assigned back into their variables, so you get -1 in those cases. The following example demonstrates this:
//: c03:URShift.java
// Test of unsigned right shift.
import com.bruceeckel.simpletest.*;
public class URShift {
static Test monitor = new Test();
public static void main(String[] args) {
int i = -1;
System.out.println(i >>>= 10);
long l = -1;
System.out.println(l >>>= 10);
short s = -1;
System.out.println(s >>>= 10);
byte b = -1;
System.out.println(b >>>= 10);
b = -1;
System.out.println(b>>>10);
monitor.expect(new String[] {
"4194303",
"18014398509481983",
"-1",
"-1",
"4194303"
});
}
} ///:~
In the last shift, the resulting value is not assigned back into b, but is printed directly, so the correct behavior occurs.
Here’s an example that demonstrates the use of all the operators involving bits:
//: c03:BitManipulation.java
// Using the bitwise operators.
import com.bruceeckel.simpletest.*;
import java.util.*;
public class BitManipulation {
static Test monitor = new Test();
public static void main(String[] args) {
Random rand = new Random();
int i = rand.nextInt();
int j = rand.nextInt();
printBinaryInt("-1", -1);
printBinaryInt("+1", +1);
int maxpos = 2147483647;
printBinaryInt("maxpos", maxpos);
int maxneg = -2147483648;
printBinaryInt("maxneg", maxneg);
printBinaryInt("i", i);
printBinaryInt("~i", ~i);
printBinaryInt("-i", -i);
printBinaryInt("j", j);
printBinaryInt("i & j", i & j);
printBinaryInt("i | j", i | j);
printBinaryInt("i ^ j", i ^ j);
printBinaryInt("i << 5", i << 5);
printBinaryInt("i >> 5", i >> 5);
printBinaryInt("(~i) >> 5", (~i) >> 5);
printBinaryInt("i >>> 5", i >>> 5);
printBinaryInt("(~i) >>> 5", (~i) >>> 5);
long l = rand.nextLong();
long m = rand.nextLong();
printBinaryLong("-1L", -1L);
printBinaryLong("+1L", +1L);
long ll = 9223372036854775807L;
printBinaryLong("maxpos", ll);
long lln = -9223372036854775808L;
printBinaryLong("maxneg", lln);
printBinaryLong("l", l);
printBinaryLong("~l", ~l);
printBinaryLong("-l", -l);
printBinaryLong("m", m);
printBinaryLong("l & m", l & m);
printBinaryLong("l | m", l | m);
printBinaryLong("l ^ m", l ^ m);
printBinaryLong("l << 5", l << 5);
printBinaryLong("l >> 5", l >> 5);
printBinaryLong("(~l) >> 5", (~l) >> 5);
printBinaryLong("l >>> 5", l >>> 5);
printBinaryLong("(~l) >>> 5", (~l) >>> 5);
monitor.expect("BitManipulation.out");
}
static void printBinaryInt(String s, int i) {
System.out.println(
s + ", int: " + i + ", binary: ");
System.out.print(" ");
for(int j = 31; j >= 0; j--)
if(((1 << j) & i) != 0)
System.out.print("1");
else
System.out.print("0");
System.out.println();
}
static void printBinaryLong(String s, long l) {
System.out.println(
s + ", long: " + l + ", binary: ");
System.out.print(" ");
for(int i = 63; i >= 0; i--)
if(((1L << i) & l) != 0)
System.out.print("1");
else
System.out.print("0");
System.out.println();
}
} ///:~
The two methods at the end, printBinaryInt( ) and printBinaryLong( ), take an int or a long, respectively, and print it out in binary format along with a descriptive string. You can ignore the implementation of these for now.
You’ll note the use of System.out.print( ) instead of System.out.println( ). The print( ) method does not emit a newline, so it allows you to output a line in pieces.
In this case, the expect( ) statement takes a file name, from which it reads the expected lines (which may or may not include regular expressions). This is useful in situations where the output is too long or inappropriate to include in the book. The files ending with “.out” are part of the code distribution, available for download from www.BruceEckel.com, so you can open the file and look at it to see what the output should be (or simply run the program yourself).
As well as demonstrating the effect of all the bitwise operators for int and long, this example also shows the minimum, maximum, +1, and -1 values for int and long so you can see what they look like. Note that the high bit represents the sign: 0 means positive and 1 means negative. The output for the int portion looks like this:
-1, int: -1, binary:
11111111111111111111111111111111
+1, int: 1, binary:
00000000000000000000000000000001
maxpos, int: 2147483647, binary:
01111111111111111111111111111111
maxneg, int: -2147483648, binary:
10000000000000000000000000000000
i, int: 59081716, binary:
00000011100001011000001111110100
~i, int: -59081717, binary:
11111100011110100111110000001011
-i, int: -59081716, binary:
11111100011110100111110000001100
j, int: 198850956, binary:
00001011110110100011100110001100
i & j, int: 58720644, binary:
00000011100000000000000110000100
i | j, int: 199212028, binary:
00001011110111111011101111111100
i ^ j, int: 140491384, binary:
00001000010111111011101001111000
i << 5, int: 1890614912, binary:
01110000101100000111111010000000
i >> 5, int: 1846303, binary:
00000000000111000010110000011111
(~i) >> 5, int: -1846304, binary:
11111111111000111101001111100000
i >>> 5, int: 1846303, binary:
00000000000111000010110000011111
(~i) >>> 5, int: 132371424, binary:
00000111111000111101001111100000
The binary representation of the numbers is referred to as signed two’s complement.
|
|