CS:APP Data Lab
restriction
- Integer constants 0 through 255 (0xFF), inclusive. You are
not allowed to use big constants such as 0xffffffff. - Function arguments and local variables (no global variables).
- Unary integer operations ! ~
- Binary integer operations & ^ | + << >>
- NOT ALLOW
- Use any control constructs such as if, do, while, for, switch, etc.
- Define or use any macros.
- Define any additional functions in this file.
- Call any functions.
- Use any other operations, such as &&, ||, -, or ?:
- Use any form of casting.
- Use any data type other than int. This implies that you
cannot use arrays, structs, or unions.
Question
q1
Description
bitXor - x^y using only ~ and &.
Example
bitXor(4, 5) = 1
Answer
Compare eq1 and eq2, eq1 uses five NOT operations and three AND operations, eq2 uses four NOT operations and three AND operations. Therefore eq2 is better.
Code
1 | int bitXor(int x, int y) { |
q2
Description
tmin - return minimum two’s complement integer.
Legal ops: ! ~ & ^ | + << >>.
Answer
For data of type int, the minimum value is which is expressed in binary as 1|0x31.
Code
1 | int tmin(void) { |
q3
Description
IsTmax - returns 1 if x is the maximum, two’s complement number, and 0 otherwise.
Legal ops: ! ~ & ^ | +.
Answer
Notice that only when x = -1 or x = INT_MAX, x^(x+1) = 0xffffffff.
Therefore, we can use a = ~(x ^ (x+1)) to find INT_MAX and -1. when a = 0, x = INT_MAX or -1.
Next step we need to distinguish INT_MAX and -1. Notice that -1 + 1 = 0. So we can use b = !(x+1) to find -1. when b = 0, x = INT_MAX; b = 1, x = -1.
At Last, we can use res = !(a | b) to check whether x is INT_MAX.
Code
1 | int isTmax(int x) { |
q4
Description
allOddBits - return 1 if all odd-numbered bits in word set to 1 where bits are numbered from 0 (least significant) to 31 (most significant).
Example
Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1.
Answer
We can use (x & 0xAAAAAAAA) ^ 0xAAAAAAAA to check if all odd-numbered bits in word set to 1.
Therefore it’s important to get a constant 0xAAAAAAAA.
Since we can only use 0 ~ 0xff, we can simply repeate flowing code four times to get 0xAAAAAAAA.
1 | key |= 0xaa; |
However, it will cost 2 * 4 = 8 operations. We can use a better way to get constant.
- get a = 0xAA.
- get b = 0xAAAA.
- get c = 0xAAAAAAAA.
It’s a bit like binary serach. Every time we can construct double length number. Therefore to build a 32 bits number, we just use operations to get the number.
Code
1 | int allOddBits(int x) { |
q5
Description
negate - return -x
Example
Example: negate(1) = -1.
Answer
Since x + ~x = -1, we can change it to -x = ~x + 1.
Code
1 | int negate(int x) |
q6
Description
isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters ‘0’ to ‘9’)
Example
isAsciiDigit(0x35) = 1.
isAsciiDigit(0x3a) = 0.
isAsciiDigit(0x05) = 0.
Answer
%TODO
Code
1 | int isAsciiDigit(int x) |
q7
Description
conditional - same as x ? y : z
Example
Example: conditional(2,4,5) = 4
Answer
At first, we can use x = !x to make .
With x = ~x + 1, we can get .
Notice that y ^ y ^ z = z, y ^ 0 = y. Therefore, we can use y ^ ((y ^ z) & x) to get answer. When x = 0, (y ^ z) & x = y ^ z, otherwise, (y ^ z) & x = 0.
Code
1 | int conditional(int x, int y, int z) |