# CS:APP Data Lab 解題筆記 ###### tags: `cs:app` Data Lab 是 CS:APP 的第一個實驗,主要是訓練學員對於位元操作、整數和浮點數的相關概念。 ## bitXor 題目需求:對兩個整數輸入 x & y, 僅使用 `~, &` 完成 XOR 函數操作 題解: 1. 先利用 ==XOR = (x & ~y) | (~x & y)== 的特性,那接下來的問題就是要如何用 `~, &` 來表示 | 2. 利用集合的概念, ==A|B = ~ (~A & ~B)== ```cpp /* * bitXor - x^y using only ~ and & * Example: bitXor(4, 5) = 1 * Legal ops: ~ & * Max ops: 14 * Rating: 1 */ int bitXor(int x, int y) { return ~(~(x & (~y)) & ~(y & (~x))); } ``` ## tmin 題目需求:回傳最小二補數整數值 題解:將 1 左移 31 即可 ```cpp /* * tmin - return minimum two's complement integer * Legal ops: ! ~ & ^ | + << >> * Max ops: 4 * Rating: 1 */ int tmin(void) { return 0x01 << 31; } ``` ## istmax 題目需求: 判斷輸入有號整數 x 是否為 Tmax 題解: 1. 利用 ==Tmax + 1 會溢出成 Tmin== 的特性, ((Tmax + 1) = Tmin) ^ Tmax + 1 = 0,對 0 做 `!` 操作為 1,寫成 bit operation 為 `!(((x+1)^x) + 1)` 2. 承 1.,由於 ==-1== 為另一個符合上述條件的數字,需要額外判斷 3. 承 2.,利用 ==!!(Tmax + 1) = !!Tmin = 1==,==!!(-1 + 1) = !! 0 = 0== 4. 將 1. & 3. 用 `&` 結合得到最終輸出 ```cpp /* * isTmax - returns 1 if x is the maximum, two's complement number, * and 0 otherwise * Legal ops: ! ~ & ^ | + * Max ops: 10 * Rating: 1 */ int isTmax(int x) { return !(((x+1)^x) + 1) & (!!(x+1)); } ``` ## allOddBits 題目要求: 判斷是否有號整數 x 的所有奇數位元都為 1 題解: 1. 先利用==奇數位元遮罩 `~evenmask = 0xAAAAAAAA`== 將 x 所有的奇數位元取出 2. 將 1. 的結果與==偶數位元的遮罩 `evenmask = 0x55555555`== 進行 ==XOR== 操作 3. 承 2. 若 x 所有==奇數位元都是 1==,則 2. 的結果一定為 ==`0xFFFFFFFF`== 4. 將 3. 的結果 `+ 1`,如果 x 所有奇數位元都是 1 的話,會==溢出變成 0== 5. 取 `!` 得到最終結果 ```cpp /* * 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) * Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 12 * Rating: 2 */ int allOddBits(int x) { int evenmask = 0x55; evenmask = evenmask | evenmask << 4; evenmask = evenmask | evenmask << 8; evenmask = evenmask | evenmask << 16; return !(((x & ~evenmask) ^ evenmask) + 1); } ``` ## negate 題目要求: 回傳 -x 題解: 利用二補數的特性,有號整數 x 的負數為 ==(not x) + 1== ```cpp /* * negate - return -x * Example: negate(1) = -1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ int negate(int x) { return ~x + 0x01; } ``` ## isAsciiDigit 題目要求: 判斷輸入有號整數是否為 ASCII code 的數字 0~9 (0x30 ~ 0x39) 題解: 1. 先觀察 `0x30` & `0x39`,將兩數表達成二進位分別是 `00110000` & `00111001`,==高位的 4 個 bit 都是 `0011`== 2. 對 `0x30` 及輸入==往右偏移 4 位==將低位捨去,==再取 XOR==確認輸入 x 的 4~7 位 bit 是否符合 `0011`,若不符合則該項為 1 `int h_bit_comp = (0x30>>4) ^ (x>>4);` 3. 接著我們再觀察 `0x3a` ~ `0x3f`,其二進位低 4 位 bit 中,==第 2 或第 3 位一定其中 1 個為 1==,==第 4 位一定為 1== 4. 承 3. - 將輸入右移 1 & 2後與 0x01(001) 取 `|` 確認 x 第 2 或第 3 位是否有 1 `int two_bit_comp = (x >> 1) & 0x01;` `int three_bit_comp = (x >> 2) & 0x01;` - 將輸入右移 3 後與 0x01(0001) 取 `&` 確認 x 第 4 位是否為 1,若為 1 則該項為 1 `int forth_bit_comp = (x >> 3) & 0x01;` 5. 對第 4. 點兩個表達式的結果先單獨取 & ,當==兩個條件都符合時,代表低 4 位大於 9== 6. 對第 2. 點及第 5. 點的結果個別取 `!` 後再取 `&` 即為答案 ```cpp /* * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9') * Example: isAsciiDigit(0x35) = 1. * isAsciiDigit(0x3a) = 0. * isAsciiDigit(0x05) = 0. * Legal ops: ! ~ & ^ | + << >> * Max ops: 15 * Rating: 3 */ int isAsciiDigit(int x) { int h_bit_comp = (0x30>>4) ^ (x>>4); int two_bit_comp = (x >> 1) & 0x01; int three_bit_comp = (x >> 2) & 0x01; int forth_bit_comp = (x >> 3) & 0x01; return !h_bit_comp & !((two_bit_comp | three_bit_comp) & forth_bit_comp); } ``` 這題覺得自己有點硬解了,所以上網查了其他人的實作,以下參考[[連結]](https://zhuanlan.zhihu.com/p/59534845) ,個人認為比較符合題目設定的目的,也解得漂亮許多: 1. 設定一個 UpperBound,讓==大於 `0x39` 的數加上它會溢出變成負數== 2. 設定一個 LowerBound,讓==小於 `0x30` 的數加上會為負數== 3. 將輸入 x 分別加上 UpperBound & LowerBound,右移 31 bit 後與 `sign` 做 `&` 操作判斷數值的正負 4. 當有任何一個數為負時,代表超出範圍,`!(upperBound|lowerBound)` 回傳 0; 反之回傳 1 ```cpp int isAsciiDigit(int x) { int sign = 0x1<<31; int upperBound = ~(sign|0x39); int lowerBound = ~0x30; upperBound = sign&(upperBound+x)>>31; lowerBound = sign&(lowerBound+1+x)>>31; return !(upperBound|lowerBound); } ``` ## conditional 題目要求: 以位元運算達到三元運算的功能 題解: 1. 當 x != 0 時,要回傳 y;反之要回傳 z,因此解題的邏輯為 y & z 各利用一個表達式計算回傳值,==符合條件的表達式回傳原數值 (y or z);不符條件的表達式回傳 0==,再用 Or 操作回傳非 0 項 2. 對 y 來說, x != 0 時要回傳 y,反之回傳 0,所以要設計一個表達式,x != 0 時為 `0xFFFFFFFF`;x = 0 時為 `0x00000000` --> `~((!x<<31)>>31)` 3. 對 z 來說, x != 0 時要回傳 0,反之回傳 z,所以要設計一個表達式,x != 0 時為 `0x00000000`;x = 0 時為 `0xFFFFFFFF` --> `((!x<<31)>>31)` 4. 將兩個表達式取 `Or` 得到最終答案 ```cpp /* * conditional - same as x ? y : z * Example: conditional(2,4,5) = 4 * Legal ops: ! ~ & ^ | + << >> * Max ops: 16 * Rating: 3 */ int conditional(int x, int y, int z) { int cond_1 = ~((!x<<31)>>31)&y; int cond_2 = ((!x<<31)>>31)&z; return cond_1 | cond_2; } ``` ## isLessOrEqual 題目要求: 利用位元運算完成 `<=` 的邏輯操作 題解: 1. 一些基本的解題方向如下 - x <= y 等同於判斷 ==x - y <= 0== 是否為真 - x - y 在==沒有溢出==的情況下可寫成 ==`x + ~y + 1`== - 承 2.,在 x & y ==同號的情況下不會溢出==,僅在不同號時有機會發生 - 因此若 x & y 不同號則改判斷是否 ==`x > 0 & y < 0`== 2. 當 x & y 同號時,我們判斷 x - y 為正或負值,另外==需考慮 `=` 的情況==,所以多了 `| !x_minus_y` `same_sign & ((x_minus_y >> 31) | !x_minus_y)` 3. 若 x & y 不同號則判斷是否 `x > 0 & y < 0` `(!(~sign_x & 0x1) & !(sign_y & 0x1))` 4. 第 2. & 3. 點有一個條件為真代表 `x <= y` ```cpp /* * isLessOrEqual - if x <= y then return 1, else return 0 * Example: isLessOrEqual(4,5) = 1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 24 * Rating: 3 */ int isLessOrEqual(int x, int y) { int sign_x = x >> 31; int sign_y = y >> 31; int same_sign = !(sign_x ^ sign_y); int x_minus_y = x + (~y) + 1; return (same_sign & ((x_minus_y >> 31) | !x_minus_y)) | (!(~sign_x & 0x1) & !(sign_y & 0x1)); } ``` ## 練習題 2.66 leftmost_one 額外練習習題 2.66,給定一個 32-bit 無號數,回傳最大 bit 為 1 的遮罩,如 0xFF00 -> 0x8000,若 x = 0 則回傳 0 1. 先將==最高位(含)以下==的位元全部以 1 填滿,透過 ==`mask |= mask >> 2^n`== 的操作,每次填滿 2 的冪次個 1,由 n = 0 -> n = 4 即可填滿 32 位元 2. 填滿後==先右移 1 個位元== (先加 1 有可能 overflow) ==再加上 1== 3. 要考慮 x 為零的情況,所以要==與自身做 `&` 運算== ```cpp int leftmost_one(unsigned x) { unsigned mask = x; mask |= mask >> 1; mask |= mask >> 2; mask |= mask >> 4; mask |= mask >> 8; mask |= mask >> 16; return x & ((mask >> 1) + 1); } int main() { printf("Left most bit of 0xff00 is 0x%x\n", leftmost_one(0xff00)); printf("Left most bit of 0x6600 is 0x%x\n", leftmost_one(0x6600)); printf("Left most bit of 0x0001 is 0x%x\n", leftmost_one(0x0001)); printf("Left most bit of 0x0000 is 0x%x\n", leftmost_one(0x0000)); } Left most bit of 0xff00 is 0x8000 Left most bit of 0x6600 is 0x4000 Left most bit of 0x0001 is 0x1 Left most bit of 0x0000 is 0x0 ``` ## logicalNeg 題目要求: 以位元操作完成 `!` 運算子,注意不得使用 `!` 題解: 1. 本題重點要利用 ==`~0 + 1 = 0`== 的特性 2. `~x + 1` 在二補數系統下,等於 x 取負號,除了 0 以外,其他==整數與其負數的最高位 bit 必定一個為 0 一個為 1== 3. 總和 1. & 2.,x = 0 時 ==`(~x + 1) | x`== 最高位一定為 0;反之一定為 1 4. 將==最高位 bit 右移 31 之後取 `~`== , x = 0 時為 `0xFFFFFFFF`;反之為 `0x00000000` 5. 將第 4. 點之結果與 ==`0x1` 取 &== 將最低位 bit 抓出來即為回傳值 ```cpp /* * logicalNeg - implement the ! operator, using all of * the legal operators except ! * Examples: logicalNeg(3) = 0, logicalNeg(0) = 1 * Legal ops: ~ & ^ | + << >> * Max ops: 12 * Rating: 4 */ int logicalNeg(int x) { return ~(((~x + 1) | x) >> 31) & 0x1; } ``` ## Reference 1. [Computer Systems: A Programmer's Perspective, 3/E (CS:APP3e)](http://csapp.cs.cmu.edu/3e/labs.html)