CSCI 2330
Foundations of Computer Systems

Bowdoin College
Fall 2019
Instructor: Sean Barker

Lab 1 - A Bit Puzzling

Assigned:Friday, September 13.
Checkpoint 1:Wednesday, September 18. At least four puzzles must be completed (including documentation).
Checkpoint 2:Sunday, September 22. At least nine puzzles must be completed (including documentation).
Due Date:Thursday, September 26. All fifteen puzzles are due.
Collaboration Policy:Level 1
Group Policy:Individual

This lab is designed to familiarize you with the bit-level representations of data used by computers. At the same time, you will become well-versed in the bitwise manipulations available in many languages (like C). 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.

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

Additionally, 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.

In order to solve the puzzles while following the rules above, you will need to have a clear understanding of data representations and be clever about using bitwise operators!

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 2'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
bitFit(x,n)2return whether x can be represented using n bits
byteSwitch(x,n,m)2swap bytes n and m of x
addOverflow(x,y)3determine if x+y causes overflow
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

The lab files have been added to your SVN directory for you -- to download them, simply do an update in your checked-out SVN directory. The *only* file you need to modify is puzzles.c.

Remember that you need to do your work on turing, not on your local machine -- since the files are compiled for Linux, they will not execute on a Mac or a Windows PC.

Your last-committed 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 your work, it's not submitted. Committing 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).

warningIMPORTANT: Make sure that you are running both blc and btest on your solutions, not just btest. If you submit functional but noncompliant solutions, you will be penalized! Using the driver.pl utility will avoid this pitfall, as it will run both checkers automatically on your solutions.

"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 (though I will be able to see your entries regardless of your nickname). 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