You may occasionally need to access and manipulate data at the
bit level. For example, each bit in a set of data might represent
the on/off state of a sensor or a pixel in a detector array. Representing
the values as bits is much more memory efficient than assigning
a full byte to each detector element if an element only holds one
of two states.
A more common application of bitwise operations is with colors.
We will see in Chapter
11 that the components RGB & alpha (red, green, blue, and
the transparency factor) for a color are packed as four byte values
into an int
value. In image processing and other graphics applications, you
can use the bitwise operators to obtain these bytes from a color
value, modify the component values, and then pack them back into
a int value.
Bitwise Operations
In Chapter
2: Operators we displayed a table of the bitwise operators
that act on individual bits in integer types. If the operator involves
two operands of different integer types, the wider type will result.
Four of the operators carry out Boolean operations on the bits:
Compliment: |
~x |
- flips each bit to the opposite
value. |
AND
|
x
& y |
- AND
operation between the corresponding bits in x
and y. |
OR |
x
| y |
- OR
operation between the corresponding bits in x
and y. |
XOR |
x
^ y |
- XOR
operation between the corresponding bits in x
and y. |
The other three bit operators involved shifting of bits:
Shift
left |
x
<< y |
- shifts
x to the left by y bits. The high order bits are lost while
zeros fill the right bits. |
Shift
Right - Signed |
x
>> y |
- shifts
x to the right by y bits. The low order bits are lost while
the sign bit value (0 for positive numbers, 1 for negative)
fills in the left bits. |
Shift
Right - Unsigned |
x
>>> y |
- shifts
x to the right by y bits. The low order bits are lost while
zeros fill in the left bits regardless of the sign. |
The following code shows how to place color component
values into an int
variable. We use the hexadecimal format for the literal constants
as a compact way to specify byte values.
int
[] aRGB = {0x56, 0x78, 0x9A, 0xBC};
int color_val = aRGB[3];
color_val = color_val | (aRGB[2] << 8);
color_val = color_val | (aRGB[1] << 16);
color_val = color_val | (aRBG[0] << 24);
This results in colorVal
holding the value: 56 78 9A BC (separating the byte values for clarity).
Similarly, to obtain a particular byte, the code goes like:
int
alpha_val = (colorVal >>> 24) & 0xFF;
int red_val = (colorVal >>> 16)
& 0xFF;
int green_val = (colorVal >>> 8) &
0xFF;
int blue_val = colorVal & 0xFF;
java.util.BitSet
The BitSet
object represents an array of bits whoses size can grow as needed.
(In that sense, it is more like a Vector
object than a Java array.) The bits represent a set of Boolean values
more efficiently by assigning a bit to each true/false value rather
than using a whole byte in an array of bytes for an array of
boolean primitive types. (JVM implementations
for boolean
type arrays differ but the Sun JVM uses a byte array.)
The BitSet
methods might also be helpful if one is handling data in which individual
bits represent information of interest. For example, each bit might
represent the state of a relay in a large group of relays. Although
internally the JVM might represent the BitSet
with an array of long
values, there is unfortunately no method in the BitSet
class that converts an array of long
values into a BitSet
or vice versa.
The BitSet
class provides methods to access a given bit in the array by an
index:
- get (int index)
- set (int index)
- clear (int
index)
- flip (int
index)
Two BitSet
arrays can undergo Boolean operations:
- and (BitSet
bitset)
- or (BitSet
set)
- xor (BitSet
set)
- andNot (BitSet
set) - clears all of the bits in the BitSet
object when the corresponding bit is set in the specified
bitset.
The class includes a number of other methods such as clone()
for making copies of a bitset, cardinality()
for the number of bits set to one, and nextSetBit(int
fromIndex) which returns the index of the next bit set to
one at fromIndex or higher.
More Bit Handling
What if a data from another computer platform or an external device
comes as int
or long
values but the bits actually represent float
or double
values. How would we change an integer type value to the corresponding
floating point type? One approach to changing types is discussed
in Chapter 9 where we use byte arrays as the destination and source
streams. Another approach is to use the methods given below in the
Float and
Double wrapper
classes:
Float
- static
int floatToIntBits(float x)
- returns an int
values whose bits match those of a float according to the IEEE
754 floating-point "single format" bit layout as described in
Chapter 2: Tech
: Floating Point.
- static
int floatToRawIntBits(float value) - same as floatToInBits(float
x) except the NaN
value can be other than the single IEEE 754 official value (0x7fc00000).
As explained in Chapter
2: Tech : Floating Point, a vlaue is NaN
if all the bits in the exponent equal 1 and any of the bits in
the significand equal 1.
- static
float intBitsToFloat(int xbits) - treats the xbits
value as though the bits represented a float
value and returns it as a float
type. That is, this method would convert the output of floatToIntBits(float)
above back to a float
value.
Double
- static
long doubleToLongBits(double x) - returns a long
value whose bits match those of the double
x value according to the IEEE 754 floating-point bit layout
as described in Chapter
2: Tech : Floating Point.
- static
long doubleToRawLongBits(double value) - same as doubleToLongBits(double
x) except the NaN
value can be other than the single IEEE 754 official value (0x7ff8000000000000L).
As explained in Chapter
2: Tech : Floating Point, a vlaue is NaN
if all the bits in the exponent equal 1 and any of the bits in
the significand equal 1.
- static
double longBitsToDouble(long bits) - treats the xbits
value as though its bits represented a double
value and returns it as a double
type. That is, this method would convert the output of doubleToLongBits(double)
above back to a double
value.
The Integer
wrapper also offers some bit handling methods:
- static
String toBinaryString(int i) - converts the value i to
a string with 0 and 1 characters but no leading zeros.
- static
int parseInt(String s, int radix) - conversely, if s
is a string representing a binary value, such as "110103",
and radix is set to 2, then the method will return an int
value equal to the binary value.
Similarly, the Long
wrapper has a two methods of the same names for converting long
values to binary strings and strings that represent binary values
to long
values.
Note that the BigInteger
class contains bitwise methods to test bits, shift bits, clear a
particular bit, and so forth. However, remember that any change
of a bit results in a new BigInteger
object since instances of this class are immutable
References & Web Resources
Latest update: Nov. 19, 2004
|