Skip to content

Handout: Lecture 3 In-Class Exercises

Work through these at the exercise breaks. Part A is pen-and-paper - number systems and binary arithmetic, no computer. Part B is at the keyboard - printing numbers in different bases and manipulating individual bits.

Try each one yourself first; we'll discuss in class, and the solutions are in a separate document afterward. Compile C with warnings on, and use unsigned int when you are treating a value as "a bag of bits":

clang -Wall -Wextra -std=c17 myprog.c -o myprog

Part A - Number systems (pen and paper)

Exercise A1 - The 0 to 15 table

Fill in the binary (4 bits) and hexadecimal columns for every value 0 through 15. This is the table worth knowing cold: 4 bits is one hex digit, so these sixteen rows are the whole alphabet of bit work.

Decimal Binary (4 bits) Hex
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Once it is filled in, check: do the binary patterns count up the way the decimal column does? Where does the second bit "turn over"? The fourth?

Exercise A2 - Bigger conversions

Convert each value into the two missing bases. These need 8 bits, so lean on the table above - each pair of hex digits is just two rows of it stuck together.

Decimal Binary (8 bits) Hex
42
1111 1111
0x80
200
0011 1010

Exercise A3 - Binary addition

Add these binary numbers by hand, carrying at 2. Keep the results to 8 bits.

  1. 0010 1101 + 0001 0011
  2. 0100 0000 + 0100 0000
  3. 1111 1111 + 0000 0001 (what happens to the 8-bit result? what is the carry off the top called?)

For each, convert the operands and your answer to decimal and check that the math agrees - except where something interesting happens in part 3.


Part B - Bits in C (at the keyboard)

Set up

mkdir -p ~/cmsc14300/lec03
cd ~/cmsc14300/lec03

Exercise B1 - Print a number in decimal, hex, and binary

Read an integer from the user and print it three ways: decimal, hexadecimal, and binary.

  • Hex is built in: use %X (and %#x if you want the 0x shown for you).
  • Binary has no portable printf specifier - write a helper that walks the bits from the most significant down, masking off one bit at a time with the shift-then-mask idiom (v >> i) & 1.
void print_binary(unsigned int v);   /* prints 32 bits, then a newline */
Enter a number: 250
decimal: 250
hex:     0xFA
binary:  00000000 00000000 00000000 11111010
  • Stretch: print the bits in groups of 4 or 8 (as above) for readability.
  • Stretch: skip the leading zeros and print from the highest set bit down, so 250 shows 11111010 rather than 32 digits.

Exercise B2 - Set, clear, toggle, test (bit masks)

Start with unsigned int flags = 0; and drive it through the four standard moves, printing in binary after each step (reuse print_binary from B1) so you can watch the bits change.

  1. Set bit 1 and bit 3. Print. (expect ...0000 1010)
  2. Clear bit 1. Print. (expect ...0000 1000)
  3. Toggle bit 0 twice. Print after each. (it should flip on, then off)
  4. Write and use a helper:
int is_set(unsigned int x, int n);   /* 1 if bit n of x is set, else 0 */

Use it to print which of bits 0 through 7 are currently on.

Reminder of the four idioms (work out why each one does what it does):

flags |=  (1 << n);    /* set    */
flags &= ~(1 << n);    /* clear  */
flags ^=  (1 << n);    /* toggle */
(flags >> n) & 1;      /* test   */

Stretch - take it further

  1. Count the set bits in an integer (its "popcount"): loop over the 32 bits and tally how many are 1.
  2. Swap two integers using only XOR, no temporary variable: a ^= b; b ^= a; a ^= b;. Convince yourself why it works.
  3. Pack and unpack an RGB color. Given r, g, b each in 0..255, build a single integer color = (r << 16) | (g << 8) | b, then recover each channel with shifts and & 0xFF.