You might find it helpful to understand the helper function print_binary()
as well as a few more bit tricks you might find helpful for this lab.ASK YOUR TA
We have included a print_binary
function, which takes an integer and outputs its binary representation. This can be useful in debugging your code, but its use is optional and all calls to the function should be commented out in your final submission. See the video link at the top of this page for usage examples.
This lab has been modified by Arrvindh Shriraman, Justin Tsia, and derived from CS:APP Textbook.
Learning Objectives:
You will solve a series of programming "bit puzzles." Many of these may seem artificial, but bit manipulations are very useful in cryptography, data encoding, implementing file formats (e.g. MP3), and certain job interviews.
bits.c
contains skeletons for the programming puzzles, along with a comment block that describes exactly what the function must do and what restrictions there are on its implementation. Your assignment is to complete each function skeleton using:
The intent of the restrictions is to require you to think about the data as bits - because of the restrictions, your solutions won't be the most efficient way to accomplish the function's goal, but the process of working out the solution should make the notion of data as bits completely clear.
These are small programs that you might useful for the task in hand.
The table below describes a set of functions that manipulate and test sets of bits. The Rating column gives the difficulty rating (the number of points) for each puzzle and the Description column states the desired output for each puzzle along with the constraints. See the comments in bits.c
for more details on the desired behavior of the functions. You may also refer to the test functions in tests.c
. These are used as reference functions to express the correct behavior of your functions, although they don't satisfy the coding rules for your functions.
Level | Function | Description |
---|---|---|
1 | bitAnd | Compute x & y using only ~ and logical OR operator . Hint: DeMorgan's Law. Demorgan's Law |
1 | bitXor | Compute x ^ y using only ~ and & . Hint: DeMorgan's Law. |
1 | thirdBits | Return an int with every third bit (starting from the least significant bit) set to 1 (i.e. 0100 1001 0010 0100 1001 0010 0100 1001). Hint: Remember the restrictions on integer constants. |
2 | getByte | Extract the n th byte from int x . Hint: Bytes are 8 bits. |
3 | logicalShift | Shift x to the right by n bits, using a logical shift. You only have access to arithmetic shifts in this function. |
3 | invert | Invert (0 ↔ 1) n bits from position p to position p+n-1 . Hint: Use a bitmask. |
4 | bang | Extra Credit Compute !x without using the ! operator. Hint: Recall that 0 is false and anything else is true. |
The following table describes a set of functions that make use of the two's complement representation of integers. Again, refer to the comments in bits.c
and the reference versions in tests.c
for more information.
Level | Function | Description |
---|---|---|
2 | sign | Return 1 if positive, 0 if zero, and -1 if negative. Hint: Shifting is the key. |
3 | fitBits | Return 1 if x can be represented as an n -bit, two's complement integer. Hint: -1 = ~0. |
3 | addOK | Return 1 if x+y can be computed without overflow. Hint: Think about what happens to sign bits during addition. |
4 | isPower2 | Extra Credit Return 1 if x is a power of 2, and 0 otherwise. |
We have included the following tools to help you check the correctness of your work:
We have included a print_binary
function, which takes an integer and outputs its binary representation. This can be useful in debugging your code, but its use is optional and all calls to the function should be commented out in your final submission. See the video link at the top of this page for usage examples.
btest
is a program that checks the functional correctness of the code in bits.c
. To build and use it, type the following two commands:
$ make
$ ./btest
Notice that you must rebuild btest
each time you modify your bits.c
file. (You rebuild it by typing make
.) You'll find it helpful to work through the functions one at a time, testing each one as you go. You can use the -f
flag to instruct btest
to test only a single function:
$ ./btest -f bitXor
You can feed it specific function arguments using the option flags -1
, -2
, and -3
:
$ ./btest -f bitXor -1 7 -2 0xf
Check the file README for documentation on running the btest
program.
We may test your solution on inputs that btest does not check by default and we will check to see that your solutions follow the coding rules.
The make
command additionally produces two helper executables called ishow
and fshow
that can be used to view conversions between decimal values and bit representations for integers and floating point numbers, respectively. For more information about using them, see the end of the README
file.
We have included a program to validate if you are following the rules specified in the comments.
dlc
is a modified version of an ANSI C compiler from the MIT CILK group that you can use to check for compliance with the coding rules for each puzzle. The typical usage is:
$ ./dlc bits.c
Note: dlc will always output the following warning, which can be ignored:
/usr/include/stdc-predef.h:1: Warning: Non-includable file <command-line> included from includable file /usr/include/stdc-predef.h.
The program runs silently unless it detects a problem, such as an illegal operator, too many operators, or non-straightline code in the integer puzzles. Running with the -e switch:
$ ./dlc -e bits.c
causes dlc
to print counts of the number of operators used by each function. Type ./dlc -help
for a list of command line options.
The dlc
program enforces a stricter form of C declarations than is the case for C++ or that is enforced by gcc
. In particular, in a block (what you enclose in curly braces) all your variable declarations must appear before any statement that is not a declaration. For example, dlc
will complain about the following code:
int foo(int x) {
int a = x;
a *= 3; /* Statement that is not a declaration */
int b = a; /* ERROR: Declaration not allowed here */
}
Instead, you must declare all your variables first, like this:
int foo(int x) {
int a = x;
int b;
a *= 3;
b = a;
}
Do NOT include the <stdio.h>
header file in bits.c
, as it confuses dlc
and results in some non-intuitive error messages. You will still be able to use printf
for debugging without including the <stdio.h>
header, although gcc
will print a warning that you can ignore.
The dlc
program will also complain about binary constants such as 0b10001000
, so avoid using them.
In your opinion, which restricted operator was the most difficult to reproduce? What was your solution and, more importantly, why does it work?
Consider the following function:
int mystery(int x) {
int mask = x >> 31;
return (x ^ mask)
+ ~mask + 1L;
}
What does this function do? Describe explicitly what each of the three lines does and what the final result is. [4 pt]
Consider the following two statements:
y = -1;
y = 0xFFFFFFFF;
Is there a difference between using these two statements in your code? Explain. If there is a difference, make sure to provide an example.