CSCI 2330
Foundations of Computer Systems

Bowdoin College
Fall 2021
Instructor: Sean Barker

Lab 1 - A Bit Puzzling

Assigned:Friday, September 10.
Checkpoint 1:Tuesday, September 14, 11:59 pm. At least four puzzles should be completed (including documentation).
Checkpoint 2:Sunday, September 19, 11:59 pm. At least nine puzzles should be completed (including documentation).
Due Date:Thursday, September 23, 11:59 pm.
Collaboration Policy:Level 1
Group Policy:Individual

This lab is designed to familiarize you with the bit-level representations of data used by computers, as well as the bitwise manipulations supported by our machines. Your task is to solve a series of puzzles implemented as short C functions in which various operations are implemented using only bitwise operators and no higher-level constructs. To do so, you will need a clear understanding of the integer and floating point representations used by modern machines as well as comfort working with the various bitwise operators.

Lab Overview

In this lab, you will modify a single C source file, puzzles.c, which is included in the starter files. This file contains the skeleton for a set of 15 programming puzzles. Each puzzle is a function which should compute a simple operation (e.g., returning whether a given number is positive or negative). Your task is to complete each function using a highly restricted subset of the full C language. In particular, all of the following are prohibited in your functions:

Individual puzzles may further restrict allowed operations, which will be noted in the puzzle description. Note that you are allowed to use assignment (=) to create variables in your functions (these will be helpful largely for the sake of readability). For example, you might write a line that assigns a subexpression to a variable, and then reuse that variable in a subsequent expression on another line. Use of variables will help prevent writing very long expressions on a single line.

Beyond the basic coding rules listed above, each function must be completed using using no more than a specified number of operators. This restriction is primarily to prevent 'brute-force' type solutions to some of the puzzles. Your goal should be to complete each puzzle using as few operators as possible. All operators except for = (assignment) count towards your total.

Floating-point puzzles: For floating point puzzles, the set of restrictions is somewhat relaxed. For these puzzles, you are allowed to use conditionals, loops, any constants, most operators, and both the int and unsigned types. You still cannot use any floating-point types, casting, functions, or macros.

Puzzle Information

Puzzles may make use of either raw bitwise values (i.e., bits not interpreted as numbers), integers, or floating-point numbers. Integers use a two's complement representation and floating point numbers use the standard IEEE floating point representation discussed (or soon to be discussed) in class.

Each puzzle is assigned a difficulty rating from 1 to 3 reflecting their relative complexity (1 = easiest, 3 = hardest). Rating 1 puzzles may be reasonably doable in only a few minutes. However, harder puzzles (especially rating 3 puzzles) will almost certainly require a non-trivial amount of thought, experimentation, and pen-and-paper exploration. Don't underestimate how long later puzzles will take based on earlier puzzles!

In addition to your code implementing each puzzle, you must provide explanations (via comments embedded in puzzles.c) of how and why each of your solutions works. Your comments should go beyond a simple code-to-English translation of the bitwise operations you used, and should clearly demonstrate that you understand why (as opposed to simply how) your solution works. For example, a comment that says "shifts the bits right by 3 and then and's by 1" simply restates the binary operations and does not add any further information. A better comment might be "extracts the fourth-lowest bit". Your comments may be written either inline within your functions or in the function headers (or a mix of both). Do not neglect your explanations, as they will factor significantly into your lab grade!

Complete reference implementations of the puzzles are provided in tests.c that make use of the entire C language. Refer to these if you have any question about how a given function is supposed to behave. However, given that these implementations do not have any restrictions, they are not likely to be especially helpful in how to actually write your functions.

Puzzle Listing

Summaries of the 15 puzzles are given below. The first 13 puzzles deal with integer representations, while the last two use floating point representations. Complete details of each puzzle are given in the puzzles.c file.

Puzzle NameRatingDescription
maxVal()1return largest 2's complement value
negCheck(x)1return whether x < 0
lsbCopy(x)1set all bits of result to least significant bit of x
andBits(x,y)1return x&y using only ~ and |
xorBits(x,y)1return x^y using only ~ and &
setThirdBits()2return value with every 3rd bit set to 1
byteExtract(x,n)2extract byte n from x
byteSwitch(x,n,m)2swap bytes n and m of x
addOverflow(x,y)2determine if x+y causes overflow
bitFit(x,n)3return whether x can be represented using n bits
shiftLogical(x,n)3return x>>n using a logical shift
not(x)3return !x without using !
signMagnitude(x)3convert from 2's complement to sign-magnitude
fp_abs(x)2(floating point) return absolute value of x
fp_twice(x)3(floating point) return 2*x

Testing

Checking your puzzle solutions can be tricky. Luckily, you will have a couple of useful tools at your disposal, described below.

ishow: Integer Representations

The ishow utility accepts an integer input (either in decimal or hex format) and outputs its signed, unsigned, and raw-bit (in hex) equivalents. For example:

$ ./ishow 20
Hex = 0x00000014, Signed = 20,  Unsigned = 20
$ ./ishow 0xffffffff
Hex = 0xffffffff, Signed = -1,  Unsigned = 4294967295

fshow: Floating-Point Representations

The fshow utility accepts either an integer or hex value representing an arbitrary bit pattern, or a float-point number, and outputs the corresponding floating point value and representation components. For example:

$ ./fshow 5.25

Floating point value 5.25
Bit Representation 0x40a80000, sign = 0, exponent = 0x81, fraction = 0x280000
Normalized.  +1.3125000000 X 2^(2)
$ ./fshow 100

Floating point value 1.401298464e-43
Bit Representation 0x00000064, sign = 0, exponent = 0x00, fraction = 0x000064
Denormalized.  +0.0000119209 X 2^(-126)

blc: Compliance

The blc (bit lab compiler) utility is a modified C compiler that will check your solutions for compliance with the coding rules specified. To check the compliance (but not correctness!) of your solutions, run blc on your puzzles.c file as follows:

$ ./blc puzzles.c

If your program is fully compliant, no output will be produced (otherwise, an error message will be printed). You can also count the number of operations in each function by passing the -e switch:

$ ./blc -e puzzles.c

btest: Correctness

To check the correctness (but not compliance) of your solutions, use the btest utility. To use btest, you must compile it using the included Makefile by typing make, which will compile it using your current puzzles.c file. This means that you must rebuild btest each time you modify puzzles.c. You should not modify the included Makefile.

Once compiled, simply run the utility directly to check the correctness of all your solutions:

$ ./btest

You can also use the -f flag to tell btest to test only a single named function, like this (assuming a function named foo):

$ ./btest -f foo

While btest normally checks your solutions against many possible inputs, you can also check specific inputs using the flags -1, -2, and -3. For example, to test the correctness of foo(5, 0xF), you could run the following:

$ ./btest -f foo -1 5 -2 0xF

driver.pl: Autoscoring

The driver.pl utility (a Perl script) will verify your solutions' compliance using blc and correctness using btest, as well as count the number of operations used. To use it, simply execute it directly:

$ ./driver.pl

The script will output an autogenerated score for each puzzle out of 1-3 possible correctness points (based on the difficulty rating of the puzzle) and 2 possible performance points (based on the number of operations used). The total score shown does not correspond directly to your lab score (in particular, a full performance score is not necessary for full credit) but will give you a sense of where improvement is possible, as well as flagging any noncompliant or nonfunctional puzzle solutions.

General Advice

Puzzle Tips

Logistics

Follow the same steps you followed in Lab 0 to initialize and clone your private lab repository from GitHub, which will contain the lab files. The only file you will modify is puzzles.c.

Remember that since the files are compiled for Linux (and will not execute on a Mac or a Windows PC), you will need to do your work on turing.

Your last-pushed puzzles.c at the lab due date (and each of the intermediate checkpoints) will constitute your lab and checkpoint submissions. Remember that if you haven't committed and pushed your work, it's not submitted. Committing and pushing frequently as a way to save your work is a good idea as well.

Evaluation

You will be evaluated on the contents of your puzzles.c file, which must contain both your puzzle solutions and your solution explanations. Do not store any work that you would like to be considered outside this file!

Your program will be evaluated on the correctness of your puzzle solutions, the number of operations used to construct your solutions, and your puzzle explanations (i.e. documentation).

Partial credit is possible for puzzle solutions that are not complete, provided some understanding of the problem is apparent (note that good documentation is doubly important here to demonstrate your understanding).

"Bit Hacker" Contest

Just for fun, as part of this lab, you may participate in a completely optional contest to compete against other students and my solution to develop the most efficient puzzle solutions. The goal of the contest is to solve each puzzle using the fewest number of operators. Students who match or beat my solution's operator count for each puzzle are awarded the Bit Hacker designation and associated bragging rights.

To submit your puzzle entries to the contest, simply call the driver with a -u flag providing a nickname that you would like to show on the scoreboard:

$ ./driver.pl -u "Your Nickname"

You may use any nickname you like and it need not identify yourself to other contest entrants. You can submit to the contest as often as you like and there is never any penalty for doing so.

Your most recent contest submission will appear on a real-time scoreboard, identified only by your nickname. Follow the link below to view the current scoreboard (only active for the duration of the lab).

Bit Hacker Scoreboard