Concepts / Converting Bitsets

Converting Bitsets

The bitset class provides two operations for these jobs, to_string() and to_ullong().

  • Programming

Predicting a Bitset's Printed Form

A bitset stores a fixed run of bits. Often the same bits are needed in another form: sometimes a program must show them as text, and sometimes it must hand the stored value back as a plain integer. The bitset class provides two operations for these jobs, to_string() and to_ullong(). Each one is designed as the reverse of a bitset constructor, so the idea of a round trip runs through this whole topic.

What do you think happens?

Given a bitset of 8 bits holding the value 5, what does to_string() print?

  • 101

  • 00000101

  • 5

  • 10100000

Reveal answer
Answer:

00000101

The bitset is 8 bits wide, so the text has exactly 8 characters. The value 5 is 101 in binary, and the leading zeros fill the remaining positions on the left. The string is written with the most significant bit leftmost.

Copying an Integer into a Bitset

The source example is a small function. It takes an int, copies it into a bitset sized to hold the whole int, and writes the bits out with to_string(). The function has two lines that matter, and they do different jobs. One line does the copying, which happens when the bitset is constructed from the int. The other line does the printing, which happens when cout writes the string that to_string() returns. Keep these two jobs separate in your mind as you trace the code.

Integer valueunsigned long longBitsetfixed width of bitsBinary textfrom to_string()Integer valuefrom to_ullong()constructorto_string()to_ullong()
How does a value move from an unsigned long long into a bitset, then into a string, and how does to_ullong() reverse that path?

Reading Characters with to_string()

In the function, the call b.to_string() produces the characters that cout then writes. The string contains only the characters 1 and 0. Because it is text, it is printed exactly as it is. It is not converted into a number along the way. The source describes the result as the bits written from left to right, which is the same direction you read a number written on paper.

The ordering follows place-value notation. The first character on the left is the most significant bit, which is the bit with the highest value. Reading left to right moves from the highest-value bit down to the lowest-value bit. The rightmost character is the least significant bit.

bit 7char 0: 0bit 6char 1: 0bit 5char 2: 0bit 4char 3: 0bit 3char 4: 1bit 2char 5: 0bit 1char 6: 1bit 0char 7: 0
Which bit index lands at which character position in the string, and why is the most significant bit leftmost?
cpp
Output
00000000000000000000000000000101
Notes on the code
  • std::bitset<8 * sizeof(int)> sets the width to the number of bits in an int, assuming a byte is 8 bits
  • The constructor copies the int value n into the bitset b
  • b.to_string() returns the bits as text, most significant bit first
  • cout writes that text unchanged

Why Leading Zeros Appear

The width of a bitset comes from its declaration, not from the value it holds. In the function above the width is 8 * sizeof(int). Because of this, the printed string always has that many characters, even when the value is small. The number of characters depends on how large int is in the environment where the code runs. The source comment states the assumption that a byte is 8 bits.

Worked example

Tracing bitset<4> holding 6

Trace the output of this code: std::bitset<4> b(6); std::cout << b.to_string();

  1. 1
    Declare the width

    The bitset has 4 bits, so to_string() will return exactly 4 characters.

  2. 2
    Copy the value

    The constructor stores the value 6 in the bitset.

  3. 3
    Write the bits from the highest position

    The value 6 is 110 in binary. Padded to 4 bits, the leftmost bit is 0, giving 0110.

  4. 4
    Print the text

    cout writes the 4 characters exactly as they are.

Result

0110

Returning to an Integer with to_ullong()

Where to_string() gives back text, to_ullong() gives back a numeric value. It is the inverse of the bitset constructor that takes an unsigned long long integer. The constructor moves a number into the bitset, and to_ullong() reads the bitset's value back out as a number. The output is an integer, not a string of characters.

The following illustration is a small example of our own, not one from the source. It builds a bitset from a string of bits and then reads the value back as an integer. The string constructor and to_string() form one pair, and the integer constructor and to_ullong() form the other.

cpp
Output
10
00001010
Notes on the code
  • The string constructor reads the characters 0 and 1 into the bitset
  • to_ullong() returns the stored value as an unsigned long long, here 10
  • to_string() returns the same bits as text, reversing the string constructor

Mistakes Beginners Make with Printed Bitsets

  • Common mistake

    Expecting to_string() to print a decimal number

    What it looks like
    bitset<8> b(5); cout << b.to_string(); prints 00000101, not 5
    Why it fails

    to_string() returns the bits as text made of 1 and 0 characters.

    Do this instead

    Use to_ullong() when you need the value as a number, or read the printed bits as binary.

  • Common mistake

    Reading the string from right to left

    What it looks like

    Treating 00000110 as if its first character were the least significant bit

    Why it fails

    The string places the most significant bit on the left, so the rightmost character is the least significant bit.

    Do this instead

    Read left to right from the highest-value bit down to the lowest-value bit.

  • Common mistake

    Forgetting the leading zeros

    What it looks like

    Expecting bitset<8> holding 5 to print as 101

    Why it fails

    The width comes from the declaration, so the output always has that many characters.

    Do this instead

    Count the declared width first, then pad the value's binary digits with zeros on the left.

Practice Tracing the Round Trip

EASY

Before running any code, predict the exact printed output of this program: std::bitset<4> b(9); std::cout << b.to_string(); Then write the result of unsigned long long v = b.to_ullong(); as a value. Compare your prediction with the trace.

Hints
  • The width is 4 bits, so the output has 4 characters.
  • The value 9 is 1001 in binary.
  • to_ullong() returns the stored value, which is still 9 because the bitset is 4 bits wide and 9 fits.

Key Takeaways

  • to_string() is the inverse of the constructor that takes a string. It returns the bitset's binary value as text, with the most significant bit leftmost.
  • to_ullong() is the inverse of the constructor that takes an unsigned long long. It returns the bitset's value as an integer.
  • The width of a bitset comes from its declaration, such as 8 * sizeof(int), so the printed output includes leading zeros.
  • To trace a conversion, follow the value from the constructor into the bitset, then through to_string() or to_ullong(), and predict the output before comparing it with the real result.

More concepts starting with C