bitset
TypeIn § 4.8 (p. 152) we covered the built-in operators that treat an integral operand as a collection of bits. The standard library defines the bitset
class to make it easier to use bit operations and possible to deal with collections of bits that are larger than the longest integral type. The bitset
class is defined in the bitset
header.
bitset
sTable 17.2 (overleaf) lists the constructors for bitset
. The bitset
class is a class template that, like the array
class, has a fixed size (§ 9.2.4, p. 336). When we define a bitset
, we say how many bits the bitset
will contain:
bitset<32> bitvec(1U); // 32 bits; low-order bit is 1, remaining bits are 0
Table 17.2. Ways to Initialize a bitset
The size must be a constant expression (§ 2.4.4, p. 65). This statement defines bitvec
as a bitset
that holds 32
bits. Just as with the elements of a vector
, the bits in a bitset
are not named. Instead, we refer to them positionally. The bits are numbered starting at 0
. Thus, bitvec
has bits numbered 0
through 31
. The bits starting at 0
are referred to as the low-order bits, and those ending at 31
are referred to as high-order bits.
bitset
from an unsigned
ValueWhen we use an integral value as an initializer for a bitset
, that value is converted to unsigned long long
and is treated as a bit pattern. The bits in the bitset
are a copy of that pattern. If the size of the bitset
is greater than the number of bits in an unsigned long long
, then the remaining high-order bits are set to zero. If the size of the bitset
is less than that number of bits, then only the low-order bits from the given value are used; the high-order bits beyond the size of the bitset
object are discarded:
// bitvec1 is smaller than the initializer; high-order bits from the initializer are discarded
bitset<13> bitvec1 (0xbeef); // bits are 1111011101111
// bitvec2 is larger than the initializer; high-order bits in bitvec2 are set to zero
bitset<20> bitvec2(0xbeef); // bits are 00001011111011101111
// on machines with 64-bit long long 0ULL is 64 bits of 0, so ~0ULL is 64 ones
bitset<128> bitvec3(~0ULL); // bits 0 ... 63 are one; 63 ... 127 are zero
bitset
from a string
We can initialize a bitset
from either a string
or a pointer to an element in a character array. In either case, the characters represent the bit pattern directly. As usual, when we use strings to represent numbers, the characters with the lowest indices in the string correspond to the high-order bits, and vice versa:
bitset<32> bitvec4("1100"); // bits 2 and 3 are 1, all others are 0
If the string
contains fewer characters than the size of the bitset
, the high-order bits are set to zero.
The indexing conventions of
string
s andbitset
s are inversely related: The character in thestring
with the highest subscript (the rightmost character) is used to initialize the low-order bit in thebitset
(the bit with subscript 0). When you initialize abitset
from astring
, it is essential to remember this difference.
We need not use the entire string
as the initial value for the bitset
. Instead, we can use a substring as the initializer:
string str("1111111000000011001101");
bitset<32> bitvec5(str, 5, 4); // four bits starting at str[5], 1100
bitset<32> bitvec6(str, str.size()-4); // use last four characters
Here bitvec5
is initialized by the substring in str
starting at str[5]
and continuing for four positions. As usual, the right-most character of the substring represents the lowest-order bit. Thus, bitvec5
is initialized with bit positions 3 through 0 set to 1100
and the remaining bits set to 0
. The initializer for bitvec6
passes a string
and a starting point, so bitvec6
is initialized from the characters in str
starting four from the end of str
. The remainder of the bits in bitvec6
are initialized to zero. We can view these initializations as
Exercises Section 17.2.1
Exercise 17.9: Explain the bit pattern each of the following
bitset
objects contains:(a)
bitset<64> bitvec(32);
(b)
bitset<32> bv(1010101);
(c)
string bstr; cin >> bstr; bitset<8>bv(bstr);
bitset
sThe bitset
operations (Table 17.3 (overleaf)) define various ways to test or set one or more bits. The bitset
class also supports the bitwise operators that we covered in § 4.8 (p. 152). The operators have the same meaning when applied to bitset
objects as the built-in operators have when applied to unsigned
operands.
Several operations—count, size, all, any
, and none
—take no arguments and return information about the state of the entire bitset
. Others—set, reset
, and flip
—change the state of the bitset
. The members that change the bitset
are overloaded. In each case, the version that takes no arguments applies the given operation to the entire set; the versions that take a position apply the operation to the given bit:
bitset<32> bitvec(1U); // 32 bits; low-order bit is 1, remaining bits are 0
bool is_set = bitvec.any(); // true, one bit is set
bool is_not_set = bitvec.none(); // false, one bit is set
bool all_set = bitvec.all(); // false, only one bit is set
size_t onBits = bitvec.count(); // returns 1
size_t sz = bitvec.size(); // returns 32
bitvec.flip(); // reverses the value of all the bits in bitvec
bitvec.reset(); // sets all the bits to 0
bitvec.set(); // sets all the bits to 1
The any
operation returns true
if one or more bits of the bitset
object are turned on—that is, are equal to 1. Conversely, none
returns true
if all the bits are zero. The new standard introduced the all
operation, which returns true
if all the bits are on. The count
and size
operations return a size_t
(§ 3.5.2, p. 116) equal to the number of bits that are set, or the total number of bits in the object, respectively. The size
function is a constexpr
and so can be used where a constant expression is required (§ 2.4.4, p. 65).
The flip, set, reset
, and test
members let us read or write the bit at a given position:
bitvec.flip(0); // reverses the value of the first bit
bitvec.set(bitvec.size() - 1); // turns on the last bit
bitvec.set(0, 0); // turns off the first bit
bitvec.reset(i); // turns off the ith bit
bitvec.test(0); // returns false because the first bit is off
The subscript operator is overloaded on const
. The const
version returns a bool
value true
if the bit at the given index is on, false
otherwise. The nonconst
version returns a special type defined by bitset
that lets us manipulate the bit value at the given index position:
bitvec[0] = 0; // turn off the bit at position 0
bitvec[31] = bitvec[0]; // set the last bit to the same value as the first bit
bitvec[0].flip(); // flip the value of the bit at position 0
~bitvec[0]; // equivalent operation; flips the bit at position 0
bool b = bitvec[0]; // convert the value of bitvec[0] to bool
bitset
The to_ulong
and to_ullong
operations return a value that holds the same bit pattern as the bitset
object. We can use these operations only if the size of the bitset
is less than or equal to the corresponding size, unsigned long
for to_ulong
and unsigned long long
for to_ullong
:
unsigned long ulong = bitvec3.to_ulong();
cout << "ulong = " << ulong << endl;
These operations throw an
overflow_error
exception (§ 5.6, p. 193) if the value in thebitset
does not fit in the specified type.
bitset
IO OperatorsThe input operator reads characters from the input stream into a temporary object of type string
. It reads until it has read as many characters as the size of the corresponding bitset
, or it encounters a character other than 1 or 0, or it encounters end-of-file or an input error. The bitset
is then initialized from that temporary string
(§ 17.2.1, p. 724). If fewer characters are read than the size of the bitset
, the high-order bits are, as usual, set to 0.
The output operator prints the bit pattern in a bitset
object:
bitset<16> bits;
cin >> bits; // read up to 16 1 or 0 characters from cin
cout << "bits: " << bits << endl; // print what we just read
bitset
sTo illustrate using bitset
s, we’ll reimplement the grading code from § 4.8 (p. 154) that used an unsigned long
to represent the pass/fail quiz results for 30 students:
bool status;
// version using bitwise operators
unsigned long quizA = 0; // this value is used as a collection of bits
quizA |= 1UL << 27; // indicate student number 27 passed
status = quizA & (1UL << 27); // check how student number 27 did
quizA &= ~(1UL << 27); // student number 27 failed
// equivalent actions using the bitset library
bitset<30> quizB; // allocate one bit per student; all bits initialized to 0
quizB.set(27); // indicate student number 27 passed
status = quizB[27]; // check how student number 27 did
quizB.reset(27); // student number 27 failed
Exercises Section 17.2.2
Exercise 17.10: Using the sequence 1, 2, 3, 5, 8, 13, 21, initialize a
bitset
that has a1
bit in each position corresponding to a number in this sequence. Default initialize anotherbitset
and write a small program to turn on each of the appropriate bits.Exercise 17.11: Define a data structure that contains an integral object to track responses to a true/false quiz containing 10 questions. What changes, if any, would you need to make in your data structure if the quiz had 100 questions?
Exercise 17.12: Using the data structure from the previous question, write a function that takes a question number and a value to indicate a true/false answer and updates the quiz results accordingly.
Exercise 17.13: Write an integral object that contains the correct answers for the true/false quiz. Use it to generate grades on the quiz for the data structure from the previous two exercises.