Assigned: | Friday, February 2. |
Checkpoint 1: | Tuesday, February 6. At least four puzzles must be completed (including documentation). |
Checkpoint 2: | Sunday, February 11. At least nine puzzles must be completed (including documentation). |
Due Date: | Thursday, February 15. All fifteen puzzles are due. |
Collaboration Policy: | Level 1 (refer to the official policy for details) |
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.
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 to use in your functions:
int
type! ~ & ^ | + << >>
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, use casting, call functions, or use 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!
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.
Summaries of the 15 puzzles are given below. Complete details of each puzzle are given in the puzzles.c
file.
Puzzle Name | Rating | Description | |
maxVal() | 1 | return largest 2's complement value | |
negCheck(x) | 1 | return whether x < 0 | |
lsbCopy(x) | 1 | set all bits of result to least significant bit of x | |
andBits(x,y) | 1 | return x&y using only ~ and | | |
xorBits(x,y) | 1 | return x^y using only ~ and & | |
setThirdBits() | 2 | return value with every 3rd bit set to 1 | |
byteExtract(x,n) | 2 | extract byte n from x | |
bitFit(x,n) | 2 | return whether x can be represented using n bits | |
byteSwitch(x,n,m) | 2 | swap bytes n and m of x | |
addOverflow(x,y) | 3 | determine if x+y causes overflow | |
shiftLogical(x,n) | 3 | return x>>n using a logical shift | |
not(x) | 3 | return !x without using ! | |
signMagnitude(x) | 3 | convert 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 |
Checking your puzzle solutions can be tricky. Luckily, you will have a couple of useful tools at your disposal.
ishow
: Integer RepresentationsThe 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 RepresentationsThe 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
: ComplianceThe 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
: CorrectnessTo 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 your 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
: AutoscoringThe driver.pl
utility (a Perl script) will both run btest
to check your solutions 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.
blc
compiler will always output the following warning, which you can safely ignore:
Non-includable file <command-line> included from includable file /usr/include/stdc-predef.h.
<stdio.h>
header file in your puzzles.c
file, as it confuses blc
and will result in non-intuitive error messages. You can still use printf
for debugging without including the <stdio.h>
header, although gcc
will print a warning that you can ignore. Here's a quick tutorial on printf if you haven't used it before (usage in C is basically identical to the Perl examples shown there).blc
program enforces a stricter form of C declarations than normally enforced by gcc
. In particular, within a given block of code (i.e., as delimited by a set of curly braces), all variable declarations must come before all other statements. For example, the following code (while valid C) will not pass blc
:
int foo(int x) { int a = x; // a declaration a *= 2; // not a declaration int b = a; // ERROR: can't put a declaration after a non-declaration }To fix the above code so that it passes
blc
, you would need to break the int b = a;
line into separate declaration and assignment lines, and put the declaration before the a *= 2;
line.andBits
and xorBits
puzzles.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.
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).
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).