Team LiB
Previous Section Next Section

17.2. The bitset Type

 

In § 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.

 

17.2.1. Defining and Initializing bitsets

 

Table 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

 
Image
 

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.

 
Initializing a bitset from an unsigned Value
 

When 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

 
Initializing a 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.

 

Image Note

The indexing conventions of strings and bitsets are inversely related: The character in the string with the highest subscript (the rightmost character) is used to initialize the low-order bit in the bitset (the bit with subscript 0). When you initialize a bitset from a string, 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

 
Image
 

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);

 

 

17.2.2. Operations on bitsets

 

The 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.

 

Table 17.3. bitset Operations

 
Image
 

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

 
Image

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_t3.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

 
Retrieving the Value of a 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;

 

Image Note

These operations throw an overflow_error exception (§ 5.6, p. 193) if the value in the bitset does not fit in the specified type.

 

 
bitset IO Operators
 

The 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 string17.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

 
Using bitsets
 

To illustrate using bitsets, 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 a 1 bit in each position corresponding to a number in this sequence. Default initialize another bitset 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.


 
Team LiB
Previous Section Next Section