/* * CS:APP Data Lab * * bits.c - Source file with your solutions to the Lab. * This is the file you will hand in to your instructor. * * WARNING: Do not include the header; it confuses the dlc * compiler. You can still use printf for debugging without including * , although you might get a compiler warning. In general, * it's not good practice to ignore compiler warnings, but in this * case it's OK. */ #include "btest.h" #include /* * Instructions to Students: * * STEP 1: Fill in the following struct with your identifying info. */ team_struct team = { /* Team name: Replace with either: Your login ID if working as a one person team or, ID1+ID2 where ID1 is the login ID of the first team member and ID2 is the login ID of the second team member */ "mconvers", /* Student name 1: Replace with the full name of first team member */ "Jay Converse", /* Login ID 1: Replace with the login ID of first team member */ "mconvers", /* The following should only be changed if there are two team members */ /* Student name 2: Full name of the second team member */ "", /* Login ID 2: Login ID of the second team member */ "" }; #if 0 /* * STEP 2: Read the following instructions carefully. */ You will provide your solution to the Data Lab by editing the collection of functions in this source file. CODING RULES: Replace the "return" statement in each function with one or more lines of C code that implements the function. Your code must conform to the following style: int Funct(arg1, arg2, ...) { /* brief description of how your implementation works */ int var1 = Expr1; ... int varM = ExprM; varJ = ExprJ; ... varN = ExprN; return ExprR; } Each "Expr" is an expression using ONLY the following: 1. Integer constants 0 through 255 (0xFF), inclusive. You are not allowed to use big constants such as 0xffffffff. 2. Function arguments and local variables (no global variables). 3. Unary integer operations ! ~ 4. Binary integer operations & ^ | + << >> Some of the problems restrict the set of allowed operators even further. Each "Expr" may consist of multiple operators. You are not restricted to one operator per line. You are expressly forbidden to: 1. Use any control constructs such as if, do, while, for, switch, etc. 2. Define or use any macros. 3. Define any additional functions in this file. 4. Call any functions. 5. Use any other operations, such as &&, ||, -, or ?: 6. Use any form of casting. You may assume that your machine: 1. Uses 2s complement, 32-bit representations of integers. 2. Performs right shifts arithmetically. 3. Has unpredictable behavior when shifting an integer by more than the word size. EXAMPLES OF ACCEPTABLE CODING STYLE: /* * pow2plus1 - returns 2^x + 1, where 0 <= x <= 31 */ int pow2plus1(int x) { /* exploit ability of shifts to compute powers of 2 */ return (1 << x) + 1; } /* * pow2plus4 - returns 2^x + 4, where 0 <= x <= 31 */ int pow2plus4(int x) { /* exploit ability of shifts to compute powers of 2 */ int result = (1 << x); result += 4; return result; } NOTES: 1. Use the dlc (data lab checker) compiler (described in the handout) to check the legality of your solutions. 2. Each function has a maximum number of operators (! ~ & ^ | + << >>) that you are allowed to use for your implementation of the function. The max operator count is checked by dlc. Note that '=' is not counted; you may use as many of these as you want without penalty. 3. Use the btest test harness to check your functions for correctness. 4. The maximum number of ops for each function is given in the header comment for each function. If there are any inconsistencies between the maximum ops in the writeup and in this file, consider this file the authoritative source. #endif /* * STEP 3: Modify the following functions according the coding rules. * * IMPORTANT. TO AVOID GRADING SURPRISES: * 1. Use the dlc compiler to check that your solutions conform * to the coding rules. * 2. Use the btest test harness to check that your solutions produce * the correct answers. Watch out for corner cases around Tmin and Tmax. */ /* * copyLSB - set all bits of result to least significant bit of x * Example: copyLSB(5) = 0xFFFFFFFF, copyLSB(6) = 0x00000000 * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ int copyLSB(int x) { /* First we clear out all of the number except the least significant * bit, then we invert the number. If it was 1, we have 111...110, * and if it was 0, we have 111...111. Adding 1 to both of these * creates the desired number. 111...110 + 1 = 111...111, and * 111...111 + 1 overflows back to 000...000. */ int f; int d; f = x & 1; d = ~f; d = d + 1; return d; } /* * evenBits - return word with all even-numbered bits set to 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 8 * Rating: 2 */ int evenBits(void) { /* 0x55 = 10101010, so to create a number with all even bits * we shift 10101010 to the left 8, 16, and 24 bits to fill out * the remainder of a 32 bit int without using 0x55555555. */ int z; int f; z = 0x55; f = (z + (z<<8) + (z<<16) + (z<<24)); return f; } /* * fitsBits - return 1 if x can be represented as an * n-bit, two's complement integer. * 1 <= n <= 32 * Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 15 * Rating: 2 */ int fitsBits(int x, int n) { /* This algorithm attempts to "slide" the number to the left and back * to the right based on the number of bits entered by the user. * If a number can fit in an n-bit number, we should be able to move * it 32-n times to the left and back to the right without changing * the value of the number. If we move back to the right and pieces * of the number are missing, or we've accidentally "dragged" * the sign over to the right when it wasn't there before, * then it can't fit. * * The right shift behaves differently than I intended it to, but * it makes the program work, so it's staying! */ int num; int equal; num = x; num = num << (32+(~n+1)); num = num >> (32+(~n+1)); equal = x ^ num; equal = equal + 1; equal = equal >> 1; return !(equal); } /* * isNotEqual - return 0 if x == y, and 1 otherwise * Examples: isNotEqual(5,5) = 0, isNotEqual(4,5) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 6 * Rating: 2 */ int isNotEqual(int x, int y) { /* Since x ^ x = 0, if x and y are equal the result will be 0, * and something else if they're not. Since we want NOT equal * to return 1, !!f flips whatever the "not equal" result was * to 0 and then back to 1 again, and if they were equal, it * flips 0 to 1 and back to 0. */ int f; f = x ^ y; return !!f; } /* * negate - return -x * Example: negate(1) = -1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ int negate(int x) { /* Takes advantage of how the negative version of a number * in 2's complement is the 1's complement + 1.*/ int f; f = ~x; f = f+1; return f; } /* * addOK - Determine if can compute x+y without overflow * Example: addOK(0x80000000,0x80000000) = 0, * addOK(0x80000000,0x70000000) = 1, * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 3 */ int addOK(int x, int y) { return 2; } /* * sum3 - x+y+z using only a single '+' * Example: sum3(3, 4, 5) = 12 * Legal ops: ! ~ & ^ | << >> * Max ops: 16 * Rating: 3 */ /* A helper routine to perform the addition. Don't change this code */ static int sum(int x, int y) { return x+y; } int sum3(int x, int y, int z) { int word1 = 0; int word2 = 0; /************************************************************** Fill in code below that computes values for word1 and word2 without using any '+' operations ***************************************************************/ /************************************************************** Don't change anything below here ***************************************************************/ return sum(word1,word2); } /* * bang - Compute !x without using ! * Examples: bang(3) = 0, bang(0) = 1 * Legal ops: ~ & ^ | + << >> * Max ops: 12 * Rating: 4 */ int bang(int x) { /* This algorithm essentially "folds" the number in on itself. * By progressively shifting the number input to the right * by smaller and smaller increments, we move the 1's if any * over to the right. Eventually the number will have a 1 in the one's * position. * * The right shift of 16 combines the first half of the number with * the second half. The last 16 bits now contain the first number, * and the first 16 bits now contain something. We shift the entire * 32 bits to the right, in effect duplicating the first 8 bits of * our new number into the last 8 bits of it. We repeat this * with the remaining halves until (assuming the input isn't 0) * we have a 1 in the last position. * * Once we have that 1 there, we add 1 to the number and then * clear out the rest of the number, since it's of no use to us. * If the input had been 0, then adding 1 has turned it into 1. * If the input had been anything else, 1 + 0 = 10, and killing the rest * of the number results in 0. */ int temp; int temp2; int mask = 1; temp = x; temp2 = x >> 16; temp = temp | temp2; temp2 = temp >> 8; temp = temp | temp2; temp2 = temp >> 4; temp = temp | temp2; temp2 = temp >> 2; temp = temp | temp2; temp2 = temp >> 1; temp = temp | temp2; temp = temp + 1; temp = temp & mask; return temp; }