CSC 161 Schedule Readings Labs & Projects Homework Deadlines Resources

Bitwise Operations and Unions

Goals

This lab provides practice in working with data at the bit level in C. Specific work involves the representation of integers, the manipulation of bits in C, and the use of unions in C to view bit patterns in multiple ways.

Bit Operations

The C programming language contains the following bitwise operations:

Bitwise Operators
Operation Meaning
& Bitwise AND
| Bitwise OR
^ Bitwise Exclusive OR (XOR)
~ Bitwise NOT
<< Shift Left
>> Shift Right

A: Binary Representation of Integers

  1. In the Lab on Integer Processing, we used the C program integer-rep.c to examine the bit representations of integers. Review that program and explain how the print_binary procedure works. As part of your explanation, include an example for the printing of the decimal number 11.
  2. In the lab on Floating-point Number Representation, we used the program data-rep.c to examine the bit representations of floating-point numbers. Review that program and use it to determine the internal representation of the values 5.0 and 11.0, as actually stored on PC/Linux computers.
  3. The program data-rep.c uses a union in C as the basis of its processing.
    1. What can be stored or accessed in a DATA type?
    2. A typedef statement allows the type union DATA to be identified more simply as a data type. Explain what types of data may be stored in variable d and how that data can be accessed.
  4. The main part of this program consists of a single loop.
    1. What is the significance of the number 1 in the while (1) expression?
    2. Under what circumstances does the program terminate, and how is this made to happen?

      Note: continue is used in place of break in the default option, so execution at that spot will jump back to the top of the loop rather than continuing with the printing that follows.

  5. Explain how the numbers are set wtih options 0 (zero), F, and I ("eye").
  6. Why is the number -1 used for option 1?
  7. After each number is set, its value is printed using several representations. While the printf statements are straightforward, the printBitGroups function may require some thought. The first use of this function comes from the call printBitGroups (d, 1). Using bitGroups as 1, trace the execution of printBitGroups.
    1. Identify the initial values of value, mask and iterations.
    2. Describe the final value of variable mask after the first loop terminates and explain how that bit pattern is achieved.
    3. Explain what processing is done in the second loop; what are the values printed and how are these values are determined?
    4. Why do you think the value variable is declared as d.integer, rather than using d.integer directly in the second loop of printBitGroups.
  8. Explain the purpose of the call printBitGroups (d, 4), and how this purpose is achieved.
    1. What is the purpose of the number 4 in this call?
    2. Describe the final value of variable mask after the first loop terminates and explain how that bit pattern is achieved.
    3. Explain what processing is done in the second loop; what are the computations in the sub-expressions inside the printf? what do they mean?
  9. Write (in English) appropriate pre- and post-conditions for function printBitGroups. These conditions should be inserted as comments to follow the function's header, but they need NOT be checked in the code using assert statements or other executed tests.

B: Bit Utilities

  1. Complete the function
    int set_bit(int word, int bit)

    that sets the bit at position bit in the value word to 1, returning the result.

    For example, set_bit(50, 3) returns 58, because in binary, the number 50=32+16+2 is represented as the 32-bit value

    0000 0000 0000 0000 0000 0000 0011 0010
    and when bit 3 is set to 1, we we have,
    0000 0000 0000 0000 0000 0000 0011 1010
    which is equivalent to adding 8.

    Bonus: Note that we may have just as easily (or perhaps more easily) invoked the function with the hexadecimal literal representation of 50, which is to say 0x32, rather than the decimal representation by writing set_bit(0x32,3). When we do so, it is often easier for the reader (and programmer) to reason more explicitly about the underlying bits.

  2. Complete the function
    int clear_bit(int word, int bit)
    that sets the bit at position bit in the value word to 0, returning the result.
  3. Complete the function
    bool test_bit(int word, int bit)
    that returns the value of the bit at position bit in the value word.
  4. Complete the function
    int toggle_bit(int word, int bit)
    that sets the bit at position bit in the value word to its complement. (Hint: Use the XOR operator.)
  5. Complete the function
    int swap_halves(int word)

    that transposes the top and bottom two bytes of the (presumed) four-byte int parameter word, returning the result.

    For example, if word has the value 0xBA5EBA77 (note that each hexadecimal digit is 4 bits), then applying swap_halves should return 0xBA77BA5E. Similarly given 0xDEADBEEF, swap_halves should return 0xBEEFDEAD.

For Those With Extra Time: More Bit Twiddling

  1. Add a menu option to the data-rep.c program, so that the integer value of variable d is changed to its ones complement value.
  2. Add a menu option to this program that begins with the value in variable d and successively toggles successive bits of the variable — printing the binary, hexadecimal, integer, and float values of the results in a table. Toggling of the bits should progress from left to right. Thus, the output might have the form:
    binary hexadecimal integer form float form
    00000000000000000000000000000000 00000000 0 0.0
    10000000000000000000000000000000 80000000 -2147483648 -0.0
    01000000000000000000000000000000 40000000 1073741824 2.0
    ...