## Linux 核心專題: Data Lab > 執行人: Terry7Wei7 > [解說影片-1](https://youtu.be/Mqs41AB9sPM) > [解說影片-2](https://youtu.be/01d1JSgaW_c) ### Reviewed by `Daniel-0224` ```c int copyLSB(int x) { - return !!(x & 1) + ~0; + return (x & 1) * -1; } ``` 改為使用 `diff` 才能顯示出增加及刪減的程式碼。 ```diff int copyLSB(int x) { - return !!(x & 1) + ~0; + return (x & 1) * -1; } ``` ### Reviewed by `david965154` >直接回傳 0X55555555 應該也可以 `int` 在 16 位元系統中並不適用直接回傳 0X55555555 參考 : [Integer (computer science)](https://en.wikipedia.org/wiki/Integer_(computer_science)) ## 任務簡介 針對 CS:APP 的 [Data Lab](https://csapp.cs.cmu.edu/3e/labs.html),提出有效的解法,規範: * 自 GitHub 上 fork [datalab](https://github.com/sysprog21/datalab),依據 [Data Lab: Manipulating Bits](http://csapp.cs.cmu.edu/3e/datalab.pdf),補完欠缺的程式碼,並且通過自動測試 * 確保儘可能少的指令,可用 `$ gcc -S bits.c` 輸出組合語言並觀察 `bits.s` 的程式碼裡頭的 x86 指令 * 避免用分支 (branch),設計時間複雜度為 $O(1)$ 的實作 * 選出其中 7 個位元操作的函式,詳細解釋其原理,需要比照 [clz 應用](https://hackmd.io/s/Bk-uxCYxz) 和 [bit-reverse](https://hackmd.io/s/ByzoiggIb) 的分析方式,==舉出真實世界中的應用案例== (最好在 Linux 核心找出相關程式碼),解說應搭配有效的測試程式碼 * 探討讓 [datalab](https://github.com/sysprog21/datalab) 得以 64-bit friendly 的提案,並舉出實際程式碼修改 參考資訊: * [CS:APP Data Lab 解題筆記](https://hackmd.io/@Chang-Chia-Chi/CSAPP_DataLab) * [Data lab 紀錄](https://hackmd.io/@justapig9020/B1VUv6wX9) * [Data Lab 作業區](https://hackmd.io/@sysprog/S1CRTc8jX) ## TODO: Data Lab 解題 > 確保儘可能少的指令 ### absolute value ```c /* * absVal - absolute value of x * Example: absVal(-1) = 1. * You may assume -TMax <= x <= TMax * Legal ops: ! ~ & ^ | + << >> * Max ops: 10 * Rating: 4 */ int absVal(int x) { int mask = x >> 31; return (x ^ mask) - mask; } ``` int mask = x >> 31; :::danger bit 的翻譯是「位元」,而非「位」,後者是量詞。 sign bit 的翻譯是「正負號位元」,避免濫用「符」這字。 參閱教育部重編國語詞典的「[符](https://dict.revised.moe.edu.tw/dictView.jsp?ID=1816)」的解釋。 ::: 將 x 右移 31 個位元,得到正負號位元。若 x 為負數,mask 為 0xFFFFFFFF(即 -1);若 x 為正數,mask 為 0x00000000。 `return (x ^ mask) - mask`; `x ^ mask`:如果 x 為負數,這將對 x 進行位元取反(相當於 `~x`);如果 x 為正數,這將保持 x 不變。 -mask:如果 x 為負數,這將再加 1,完成補數運算;如果 x 為正數,則減 0,則不會改變值。 ### addOK ```c /* * 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) { int sum = x + y; int sx = x >> 31; int sy = y >> 31; int ss = sum >> 31; int same_sign = !(sx ^ sy); int overflow = (sx ^ ss); return !(same_sign & overflow); } ``` :::danger 避免僅進行程式碼逐行解說 (ChatGPT 也會!),而是你該從程式碼設計者的角度,闡述「你為何當初想要這樣作?」「更早之前我嘗試用別的方式,遇到什麼問題」等高階想法,避免淪落為「舉燭」。 ::: `int sx = x >> 31;` 和 `int sy = y >> 31;` 取得 x 和 y 的正負號位元。若 x 為正,sx 為 0;若 x 為負,sx 為 -1(即所有位元都為1)。 `int sum = x + y;` 計算 x 和 y 的和。 `int ss = sum >> 31;` 取得 sum 的正負號位元。 `int same_sign = !(sx ^ sy);` 檢查 x 和 y 的正負號位元是否相同。若相同,則 same_sign 為 1;否則為 0。 `int overflow = (sx ^ ss);` 檢查 x 的正負號位元與 sum 的正負號位元是否不同。如果不同,則 overflow 為 1;否則為 0。 `return !(same_sign & overflow);` 如果 same_sign 和 overflow 都為 1,表示發生了溢出,回傳 0;否則回傳 1。 ### allEvenBits ```c /* * allEvenBits - return 1 if all even-numbered bits in word set to 1 * where bits are numbered from 0 (least significant) to 31 (most significant) * Examples allEvenBits(0xFFFFFFFE) = 0, allEvenBits(0x55555555) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 12 * Rating: 2 */ int allEvenBits(int x) { // Define the mask with all even bits set to 1 int mask = 0x55555555; // Check if all even bits in x are set to 1 // (x & mask) should equal to mask if all even bits are 1 return !((x & mask) ^ mask); } ``` :::danger 避免僅進行程式碼逐行解說 (ChatGPT 也會!),而是你該從程式碼設計者的角度,闡述「你為何當初想要這樣作?」「更早之前我嘗試用別的方式,遇到什麼問題」等高階想法,避免淪落為「舉燭」。 ::: `int mask = 0x55555555;` 定義一個位元遮罩,其所有偶數位元為1。 `(x & mask)` 將 x 和 mask 進行位元運算,保留 x 中的所有偶數位元。 `((x & mask) ^ mask)` 將結果與 mask 進行異或運算。如果 x 的所有偶數位元都是1,則 (x & mask) 應該等於 mask,因此異或的結果為0。 `!((x & mask) ^ mask)` 將異或的結果取反。如果異或結果為0,則表示 x 的所有偶數位都為1,取反後得到1;否則得到0。 ### anyEvenBit ```c /* * anyEvenBit - return 1 if any even-numbered bit in word set to 1 * where bits are numbered from 0 (least significant) to 31 (most significant) * Examples anyEvenBit(0xA) = 0, anyEvenBit(0xE) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 12 * Rating: 2 */ int anyEvenBit(int x) { // Define the mask with all even bits set to 1 int mask = 0x55555555; // Check if any even bit in x is set to 1 // (x & mask) should be non-zero if any even bit is 1 return !!(x & mask); } ``` :::danger 避免僅進行程式碼逐行解說 (ChatGPT 也會!),而是你該從程式碼設計者的角度,闡述「你為何當初想要這樣作?」「更早之前我嘗試用別的方式,遇到什麼問題」等高階想法,避免淪落為「舉燭」。 ::: `int mask = 0x55555555;` 定義了一個位元遮罩,其所有偶數位元為 1。 `(x & mask)` 將 x 和 mask 進行位元運算,保留 x 中的所有偶數位元。 `!!(x & mask)` 將結果進行邏輯非運算兩次。第一次邏輯非將非零值轉換為 0,將零值轉換為 1。第二次邏輯非將其結果再次取反,即將非零值轉換為 1,將零值保持為 0。最終,如果有任意一個偶數位為 1,則回傳 1;否則回傳 0。 ### anyOddBit ```c /* * anyOddBit - return 1 if any odd-numbered bit in word set to 1 * where bits are numbered from 0 (least significant) to 31 (most significant) * Examples anyOddBit(0x5) = 0, anyOddBit(0x7) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 12 * Rating: 2 */ int anyOddBit(int x) { // Define the mask with all odd bits set to 1 int mask = 0xAAAAAAAA; // Check if any odd bit in x is set to 1 // (x & mask) should be non-zero if any odd bit is 1 return !!(x & mask); } ``` :::danger 避免僅進行程式碼逐行解說 (ChatGPT 也會!),而是你該從程式碼設計者的角度,闡述「你為何當初想要這樣作?」「更早之前我嘗試用別的方式,遇到什麼問題」等高階想法,避免淪落為「舉燭」。 ::: `int mask = 0xAAAAAAAA;` 定義一個位元遮罩,其所有奇數位為1。 `(x & mask)` 將 x 和 mask 進行位元運算,保留 x 中的所有奇數位元。 `!!(x & mask)` 將結果進行邏輯非運算兩次。第一次邏輯非將非零值轉換為0,將零值轉換為1。第二次邏輯非將其結果再次取反,即將非零值轉換為1,將零值保持為0。最終,如果有任意一個奇數位為1,則回傳1;否則回傳0。 ### bang ```c /* * bang - Compute !x without using ! * Examples: bang(3) = 0, bang(0) = 1 * Legal ops: ~ & ^ | + << >> * Max ops: 12 * Rating: 4 */ int bang(int x) { // x | -x will set the highest bit to 1 if x is not 0 // Shift 31 bits to get the sign bit, negate + 1 to get the logical negation // result return ((x | (~x + 1)) >> 31) + 1; } ``` 這一題可以用到的特性 Tmax + 1 = 0 x | (~x + 1) ~x + 1 計算 -x(即 x 的二進位補數表示)。 對於非零的 x,x 和 -x 至少一個最高位元(正負號位元)為 1,因此 x | -x 的最高位元為 1。 ((x | (~x + 1)) >> 31) 右移 31 位元,將正負號位元移到最低位元。若最高位元為 1,結果為 -1(即所有位元都為1);若最高位元為 0,則結果為 0。 +1 取反加 1(或簡單取反即可)。對於非零的 x,((x | (~x + 1)) >> 31) + 1 將給出 0;對於 0,結果為 1。 ### bitAnd ```c /* * bitAnd - x&y using only ~ and | * Example: bitAnd(6, 5) = 4 * Legal ops: ~ | * Max ops: 8 * Rating: 1 */ int bitAnd(int x, int y) { return ~(~x | ~y); } ``` 這題參考笛摩根定律 ### bitCount ```c /* * bitCount - returns count of number of 1's in word * Examples: bitCount(5) = 2, bitCount(7) = 3 * Legal ops: ! ~ & ^ | + << >> * Max ops: 40 * Rating: 4 */ int bitCount(int x) { // Masks for bit manipulation int mask1 = 0x55555555; // 01010101010101010101010101010101 int mask2 = 0x33333333; // 00110011001100110011001100110011 int mask4 = 0x0F0F0F0F; // 00001111000011110000111100001111 int mask8 = 0x00FF00FF; // 00000000111111110000000011111111 int mask16 = 0x0000FFFF; // 00000000000000001111111111111111 // Step 1: Pair-wise count x = (x & mask1) + ((x >> 1) & mask1); // Step 2: 4-bit counts x = (x & mask2) + ((x >> 2) & mask2); // Step 3: 8-bit counts x = (x & mask4) + ((x >> 4) & mask4); // Step 4: 16-bit counts x = (x & mask8) + ((x >> 8) & mask8); // Step 5: 32-bit counts x = (x & mask16) + ((x >> 16) & mask16); return x; } ``` 逐步計算 1 的個數 :::danger 避免僅進行程式碼逐行解說 (ChatGPT 也會!),而是你該從程式碼設計者的角度,闡述「你為何當初想要這樣作?」「更早之前我嘗試用別的方式,遇到什麼問題」等高階想法,避免淪落為「舉燭」。 ::: 使用遮罩和移位操作逐步累積每組的 1 的個數。 `x = (x & mask1) + ((x >> 1) & mask1);` : 每 2 個位元為一組,計算每組的 1 的個數。 `x = (x & mask2) + ((x >> 2) & mask2);` : 每 4 個位元為一組,計算每組的 1 的個數。 `x = (x & mask4) + ((x >> 4) & mask4);` : 每 8 個位元為一組,計算每組的 1 的個數。 `x = (x & mask8) + ((x >> 8) & mask8);` : 每 16 個位元為一組,計算每組的 1 的個數。 `x = (x & mask16) + ((x >> 16) & mask16);` : 每 32 個位元一組,計算每組的 1 的個數。 ### bitMatch ```c /* * bitMatch - Create mask indicating which bits in x match those in y * using only ~ and & * Example: bitMatch(0x7, 0xE) = 0x6 * Legal ops: ~ & * Max ops: 14 * Rating: 1 */ int bitMatch(int x, int y) { int sameBits = x & y; int diffBits = ~x & ~y; return ~(~sameBits & ~diffBits); } ``` 若位元 x 和 y 在某個位置相同,結果遮罩的該位置應為 1,否則應為 0。 int sameBits = x & y; 計算 x 和 y 中相同位元的情況。 int diffBits = ~x & ~y; 計算 x 和 y 中不同位元的情況。 遇到位元配對題型可以聯想到 xor、xnor 來輸出,但題目限制所以可以試著拆解 特性: xor 奇數一為一,偶數一為零 xnor 偶數一為一,奇數一為零 這邊用 xnor 來實作看看 $Y = \overline{A \oplus B}$ $Y = A \cdot B + \overline{A} \cdot \overline{B}$ 但題型不能使用 or 閘,改成 $Y = \overline{\overline{A \cdot B} \cdot \overline{\overline{A} \cdot \overline{B}}}$ $=>$ $Y = \overline{\overline{sameBits} \cdot \overline{diffBits}}$ ### bitNor ``` /* * bitNor - ~(x|y) using only ~ and & * Example: bitNor(0x6, 0x5) = 0xFFFFFFF8 * Legal ops: ~ & * Max ops: 8 * Rating: 1 */ int bitNor(int x, int y) { return ~x & ~y; } ``` $Y = \overline{A+B}$ $Y = {\overline{A}} \cdot {\overline{B}}$ ### bitOr ```c /* * bitOr - x|y using only ~ and & * Example: bitOr(6, 5) = 7 * Legal ops: ~ & * Max ops: 8 * Rating: 1 */ int bitOr(int x, int y) { return ~(~x & ~y); } ``` $Y = A+B$ $Y = \overline{\overline{A+B}}$ $Y = \overline{\overline{A}\cdot\overline{B}}$ ### bitParity ```c /* * bitParity - returns 1 if x contains an odd number of 0's * Examples: bitParity(5) = 0, bitParity(7) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 4 */ int bitParity(int x) { x ^= x >> 16; x ^= x >> 8; x ^= x >> 4; x ^= x >> 2; x ^= x >> 1; return x & 1; } ``` 利用異或運算來逐步減少 x 中 1 的個數,直到只剩下一個 1。如果最終結果為 1,則表示 x 中包含奇數個 0,否則表示包含偶數個 0。 x ^= x >> 16;:將 x 右移16位,與原來的 x 異或,將高16位和低16位進行異或。 x ^= x >> 8;:將 x 右移8位,與上一步結果異或,將高8位和低8位進行異或。 x ^= x >> 4;:將 x 右移4位,與上一步結果異或,將高4位和低4位進行異或。 x ^= x >> 2;:將 x 右移2位,與上一步結果異或,將高2位和低2位進行異或。 x ^= x >> 1;:將 x 右移1位,與上一步結果異或,將高1位和低1位進行異或。 return x & 1;:檢查最終 x 的最低位元是否為 1,如果是則回傳 1,否則回傳 0。 ### bitReverse ```c /* * bitReverse - Reverse bits in a 32-bit word * Examples: bitReverse(0x80000002) = 0x40000001 * bitReverse(0x89ABCDEF) = 0xF7D3D591 * Legal ops: ! ~ & ^ | + << >> * Max ops: 45 * Rating: 4 */ int bitReverse(int x) { int mask1 = 0x55555555; // 01010101010101010101010101010101 int mask2 = 0x33333333; // 00110011001100110011001100110011 int mask3 = 0x0F0F0F0F; // 00001111000011110000111100001111 int mask4 = 0x00FF00FF; // 00000000111111110000000011111111 int mask4 = 0x0000FFFF; // 00000000000000001111111111111111 x = ((x >> 1) & mask1) | ((x & mask1) << 1); x = ((x >> 2) & mask2) | ((x & mask2) << 2); x = ((x >> 4) & mask3) | ((x & mask3) << 4); x = ((x >> 8) & mask4) | ((x & mask4) << 8); x = (x >> 16) & mask5 | ((x & mask5) << 16); return x; } ``` 將整數分成兩半,交換左右兩半的位元。 繼續將每半部分成更小的部分並交換,直到所有位元都被交換。 x = ((x >> 1) & m1) | ((x & m1) << 1); 交換每一個相鄰的位元。 x = ((x >> 2) & m2) | ((x & m2) << 2); 交換每兩個相鄰的位元。 x = ((x >> 4) & m4) | ((x & m4) << 4); 交換每四個相鄰的位元。 x = ((x >> 8) & m8) | ((x & m8) << 8); 交換每八個相鄰的位元。 x = ((x >> 16) & m16) | ((x & m16) << 16); 交換每十六個相鄰的位。 ### bitXor(x,y) ```c /* * bitXor - x^y using only ~ and & * Example: bitXor(4, 5) = 1 * Legal ops: ~ & * Max ops: 14 * Rating: 1 */ //Find only one bit in X and Y that is 1, assuming X=1010, Y=1011 //~(x&~y) == 1010&0100 = 0000, negation=1111, //~(~x&y) == 0101&1011 = 0001, negation=1110, //~(~(x&~y)&~(~x&y)) == 1111&1110 = 1110, negation=0001 int bitXor(int x, int y) { return ~(~(x&~y)&~(~x&y)); } ``` | A | B |$$Y=A \oplus B$$| |---|---| -------- | | 0 | 0 | 0 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 1 | 1 | 0 | ${Y=A\cdot {\overline {B}}+{\overline {A}}\cdot B}$ :::danger 在 108 課綱,譯作「笛摩根定律」 ::: <s>第摩根定理</s> 互換 ${Y=\overline{{\overline {A\cdot {\overline {B}}}}\cdot{\overline {{\overline {A}}\cdot B}}}}$ ### byteSwap ```c /* * byteSwap - swaps the nth byte and the mth byte * Examples: byteSwap(0x12345678, 1, 3) = 0x56341278 * byteSwap(0xDEADBEEF, 0, 2) = 0xDEEFBEAD * You may assume that 0 <= n <= 3, 0 <= m <= 3 * Legal ops: ! ~ & ^ | + << >> * Max ops: 25 * Rating: 2 */ int byteSwap(int x, int n, int m) { int n_offset = n << 3; // n * 8 int m_offset = m << 3; // m * 8 int byte_n = (x >> n_offset) & 0xFF; // Extract nth byte int byte_m = (x >> m_offset) & 0xFF; // Extract mth byte // Clear nth and mth bytes in x x = x & ~(0xFF << n_offset); x = x & ~(0xFF << m_offset); // Insert swapped bytes into x x = x | (byte_n << m_offset); x = x | (byte_m << n_offset); return x; } ``` 計算位元組偏移量 n_offset = n << 3;:將 n 左移3位,相當於乘以8,得到第 n 個位元組的偏移量。 m_offset = m << 3;:將 m 左移3位,相當於乘以8,得到第 m 個位元組的偏移量。 擷取位元組 byte_n = (x >> n_offset) & 0xFF;:將 x 右移 n_offset 位,然後與 0xFF 進行與操作,提取第 n 個位元組。 byte_m = (x >> m_offset) & 0xFF;:將 x 右移 m_offset 位,然後與 0xFF 進行與操作,提取第 m 個位元組。 清除和插入位元組 清除第 n 和第 m 個位元組:x = x & ~(0xFF << n_offset); 和 x = x & ~(0xFF << m_offset); 插入交換後的位元組:x = x | (byte_n << m_offset); 和 x = x | (byte_m << n_offset); ### tmin() ```c /* * tmin - return minimum two's complement integer * Legal ops: ! ~ & ^ | + << >> * Max ops: 4 * Rating: 1 */ int tmin(void) { return 0x1 << 31; } ``` int 類型是 32 位元,即 4 bytes。二補數最小值就是正負號位元為 1,其餘全為 0。 對 0x1 進行位移運算。 ### istmax ```c /* * 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) { int y = x + 1; // y = x + 1 = 1000 x = x + y; // x = x + y = 1111 x = ~x; // 0000 y = !y; // 0 ,y is 0 only if x was Tmax x = x | y; // 0 ,combine both conditions return !x; // return 1 if x is Tmax, 0 otherwise } ``` 以 4 bits,二補數表示法演示 | 十進位 | 二補數 | |---|----| |7 |0111| |6 |0110| |5 |0101| |4 |0100| |3 |0011| |2 |0010| |1 |0001| |0 |0000| |-1 |1111| |-2 |1110| |-3 |1101| |-4 |1100| |-5 |1011| |-6 |1010| |-7 |1001| |-8 |1000| 可以看出 0111(7) 為最大值,1000(-8) 為最小值 利用 0111 + 1 會溢出成為 1000 的特性 如果是最大值 0111 與最小值 1000 相加,會等於 1111(-1) ### allOddBits ```c /* * 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) { // 0xAAAAAAAA is a constant where all odd-numbered bits are 1 int mask = 0xAAAAAAAA; // Perform bitwise AND with the mask and compare with the mask return !((x & mask) ^ mask); } ``` 判斷是否有號數 x 的所有奇數位元都為 1 先利用奇數位元遮罩 0xAAAAAAAA 做 & 運算,留下奇數位元,將偶數位元變 0 接著做位元運算 xor,如果奇數位元都是 1 的話會與奇數位元遮罩相等,所以會是 0 如果不是 0,代表奇數位元不全都是 1 ### negate(x) ```c /* * negate - return -x * Example: negate(1) = -1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ int negate(int x) { return ~x+1; } ``` 二補數,對 x 取反向 +1 ex: 0001(1) 取反 = 1110,加 1 = 1111(-1) 1111(-1) 取反 = 0000,加 1 = 0001(1) ### isAsciiDigit(x) ```c /* * 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 mask = 0x30; //filter out ASCII codes for characters '0' to 'f' int high_mask = 0x39; int num = !((mask>>4) ^ (x>>4)); // if x = '0' to 'f' return 0 int dis = !((x) +(~ (high_mask))); // if x < 'a' return 0 return !(num & dis); } ``` 原本想先篩選掉 x 不是 0x3 開頭的, 接著透過 x 減去 9,看是否 >0 但第二段還沒想出怎麼做 看了其他人的解法 設定一個 UpperBound,讓大於 0x39 的數加上它會溢出變成負數 設定一個 LowerBound,讓小於 0x30 的數加上會為負數 將輸入 x 分別加上 UpperBound & LowerBound,右移 31 bit 後與 sign 做 & 操作判斷數值的正負 當有任何一個數為負時,代表超出範圍,!(upperBound|lowerBound) 回傳 0; 反之回傳 1 ```c 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(x, y, z) ```c /* * conditional - same as x ? y : z * Example: conditional(3,4,5) = 4 * Legal ops: ! ~ & ^ | + << >> * Max ops: 16 * Rating: 3 */ int conditional(int x, int y, int z) { x = !!x; x = ~x+1; return (x&y)|(~x&z); } ``` 題意為若 X 為真(即非 0 ),則輸出 Y,反之輸出 Z 這邊用兩層反向邏輯判斷 X,如果輸入是非 0,最後 x 會是 0xFFFFFFFFE + 1 = 0xFFFFFFFFF,如果輸入是 0,最後 x 會是 0xFFFFFFFFF + 1 = 溢位 0x00000000,再跟 Y,Z 做位元運算 ### countLeadingZero? ```c /* * countLeadingZero - count the number of zero bits preceding the * most significant one bit * Example: countLeadingZero(0x00000F00) = 20, * countLeadingZero(0x00000001) = 31 * Legal ops: ! ~ & ^ | + << >> * Max ops: 50 * Rating: 4 */ int countLeadingZero(int x) { int mask, count; // Ensure the bits of x are in the MSB position mask = ~(x >> 1); // mask becomes 0xFFFFFFFF if x is not zero x |= mask; // set all bits to 1 after the MSB // Now count leading zeros using a series of bit shifts count = !(x & (0xFFFF8000 << 16)) << 4; count |= !(x & (0xFF800000 << (count + 8))) << 3; count |= !(x & (0xF8000000 << (count + 4))) << 2; count |= !(x & (0xE0000000 << (count + 2))) << 1; count |= !(x & (0xC0000000 << (count + 1))); return count; } ``` ### copyLSB? ```c /* * 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) { - return !!(x & 1) + ~0; + return (x & 1) * -1; } ``` x & 1 取得 x 的 LSB。如果 x 的 LSB 是 1,則 x & 1 結果是 1;如果 x 的 LSB 是 0,則 x & 1 結果是 0。 將 x & 1 乘以 -1。如果x & 1 是1,則乘以-1 的結果是-1(在32 位元整數中表示為0xFFFFFFFF);如果x & 1 是0,則乘以-1 的結果是0(在32 位元整數中表示為0x00000000)。 ### distinctNegation ```c /* * distinctNegation - returns 1 if x != -x. * and 0 otherwise * Legal ops: ! ~ & ^ | + * Max ops: 5 * Rating: 2 */ int distinctNegation(int x) { return x != -x; } ``` 對於整數 x,只有當 x = 0 時,x == -x ,因為在二補數表示中,只有零的相反數是它自己。 x != -x:利用比較運算子判斷 x 是否不等於它的相反數。如果 x 不等於 -x,則回傳1;否則回傳0。 ### dividePower2 ```c /* * dividePower2 - Compute x/(2^n), for 0 <= n <= 30 * Round toward zero * Examples: dividePower2(15, 1) = 7, dividePower2(-33, 4) = -2 * Legal ops: ! ~ & ^ | + << >> * Max ops: 15 * Rating: 2 */ int dividePower2(int x, int n) { int sign = x >> 31; // sign is either 0 or -1 (0xFFFFFFFF) // For positive x, directly shift right by n // For negative x, add (1 << n) - 1 before shifting right by n return (x + (sign & ((1 << n) + ~0))) >> n; } ``` int sign = x >> 31;:計算 x 的符號位,如果 x 是正數,sign 為 0;如果 x 是負數,sign 為 -1。 (1 << n) + ~0:計算 $2^n −1$,這是為了在對負數 x 進行右移操作之前,先將其調整為向零舍入。 (sign & ((1 << n) + ~0)):根據 sign 的值選擇性地加上 $2^n −1$。若 x 是正數,sign 為0,加法不會改變結果;若 x 是負數,sign 為全1,相當於加上 $2^n −1$。 $>>n$:右移 n 位,實現除以 $2^n−1$的操作,同時確保向零舍入[捨入誤差](https://zh.wikipedia.org/zh-tw/%E6%8D%A8%E5%85%A5%E8%AA%A4%E5%B7%AE)。 可以理解成LSB的權重是 $2^0 = 1$,所以他是影響 X 是不是奇數的因素,其他位元皆可被 2 整除,所以我們在右移的時候要計算偏移量,將結果 +1 #### 為什麼只有在 X 為負數的時候需要考慮這個問題 右移操作對正數的行為 對於正數,右移操作相當於整數除以 2 的對應次方並向下取整。例如: ```C int x = 8; // 0b00001000 int result = x >> 1; // 0b00000100, result = 4 int x = 9; // 0b00001001 int result = x >> 1; // 0b00000100, result = 4 ``` 上面的程式碼中,9 右移 1 位的結果是 4,相當於 9 除以 2 的 1 次方(向下取整)。 ```c int x = -8; // 0b11111000 int result = x >> 1; // 0b11111100, result = -4 int x = -9; // 0b11110111 int result = x >> 1; // 0b11111011, result = -5 ``` 向下取整與向零取整 向下取整和向零取整的差別在於: 向下取整(floor):結果是小於或等於輸入值的最大整數。 向零取整(truncate):結果是絕對值較小的整數,也就是直接去掉小數部分。 對於正數,向下取整和向零取整的結果是相同的。 ### evenBits ```C /* * evenBits - return word with all even-numbered bits set to 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 8 * Rating: 1 */ int evenBits(void) { int pattern = 0x55; // Binary: 01010101 int mask = (pattern << 8) | pattern; // Binary: 0101010101010101 return (mask << 16) | mask; // Binary: 01010101010101010101010101010101 } ``` 直接回傳 0X55555555 應該也可以 ### ezThreeFourths ```C /* * ezThreeFourths - multiplies by 3/4 rounding toward 0, * Should exactly duplicate effect of C expression (x*3/4), * including overflow behavior. * Examples: ezThreeFourths(11) = 8 * ezThreeFourths(-9) = -6 * ezThreeFourths(1073741824) = -268435456 (overflow) * Legal ops: ! ~ & ^ | + << >> * Max ops: 12 * Rating: 3 */ int ezThreeFourths(int x) { int mult3 = (x << 1) + x; // Equivalent to x * 3 int sign = mult3 >> 31; // Sign of mult3 (either 0 or -1) // For positive x, just divide by 4 // For negative x, add 3 to mult3 before dividing by 4 to handle rounding // toward zero return (mult3 + (sign & 3)) >> 2; } ``` int mult3 = (x << 1) + x;:將 x 左移1位元得到 x * 2,再加上 x 得到 x * 3。 int sign = mult3 >> 31;:取得 mult3 的符號位,如果 mult3 是正數,sign 為0;如果 mult3 是負數,sign 為-1。 (mult3 + (sign & 3)) >> 2: mult3 + (sign & 3):對於正數 x,(sign & 3) 結果為0,不影響結果;對於負數 x,(sign & 3) 結果為 3,相當於在 mult3 上加上3,用於向零舍入。 $>> 2$:將結果右移 2 位,即將 mult3 除以4, ### fitsBits? ```C /* * 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) { int sign = x >> 31; // For positive numbers, check if they are in the range [0, 2^(n-1)-1] // For negative numbers, check if they are in the range [-2^(n-1), -1] return !(~(x >> (n + ~0)) & sign) & !(x >> (n + ~0) & !sign); } ``` ### isLessOrEqual(x,y) ```c /* * 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) { // Compute the sign bits of x and y int signX = x >> 31 & 1; int signY = y >> 31 & 1; // Case 1: x is negative, y is positive (x <= y is true) int xNegYPos = signX & ~signY; // Case 2: Both have the same sign int sameSign = !(signX ^ signY); // Calculate y - x int diff = y + (~x + 1); int diffSign = diff >> 31 & 1; // If sameSign is 1, we check the sign of the difference int lessOrEqualSameSign = sameSign & ~diffSign; // Combine the cases return xNegYPos | lessOrEqualSameSign; } ``` 這題要比較 x 是否小於等於 y 我的想法是先比較 x,y 的符號位元 若 x 是負數 sign x 為 1,y 是正數 sign y 為 0,result = 1 若符號位元相同,則再進行減法比較,若 y-x 為正,result = 1 ### logicalNeg(x) ```c /* * 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|(~x+1))>>31)+1; } ``` 透過與自身 二補數相加會產生溢位得到 0 的特性完成 除了 0 和最小數的補數是自身,但一樣能透過篩選符號位元完成 ### howManyBits(x) ```c /* howManyBits - return the minimum number of bits required to represent x in * two's complement * Examples: howManyBits(12) = 5 * howManyBits(298) = 10 * howManyBits(-5) = 4 * howManyBits(0) = 1 * howManyBits(-1) = 1 * howManyBits(0x80000000) = 32 * Legal ops: ! ~ & ^ | + << >> * Max ops: 90 * Rating: 4 */ int howManyBits(int x) { int b16,b8,b4,b2,b1,b0; int sign=x>>31; x = (sign&~x)|(~sign&x); b16 = !!(x>>16)<<4; x = x>>b16; b8 = !!(x>>8)<<3; x = x>>b8; b4 = !!(x>>4)<<2; x = x>>b4; b2 = !!(x>>2)<<1; x = x>>b2; b1 = !!(x>>1); x = x>>b1; b0 = x; return b16+b8+b4+b2+b1+b0+1; } ``` b16 = !!(x >> 16) << 4; 檢查高 16 位是否有 1,如果有,b16 為 16。 x = x >> b16; 如果 b16 為 16,則右移 16 位,否則不變。 重複這個過程對 b8、b4、b2、b1 進行檢查和位移。 b0 = x; 獲取剩餘的最低位 將所有計算出的位數加起來,並加上1表示符號位,得到最終的位數。 ### floatScale2(f) ```c /* * floatScale2 - Return bit-level equivalent of expression 2*f for * floating point argument f. * Both the argument and result are passed as unsigned int's, but * they are to be interpreted as the bit-level representation of * single-precision floating point values. * When argument is NaN, return argument * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 30 * Rating: 4 */ unsigned floatScale2(unsigned uf) { // Extract sign bit, exponent bit and mantissa bit unsigned sign = uf >> 0x80000000; unsigned exp = (uf & 0x7F800000) >> 23; unsigned frac = uf & 0x007FFFFF; // If the exponent bit is 255 (that is, all 1), it is infinity or NaN, and uf is returned directly. if (exp == 255) { return uf; } if (exp == 0) { // Shift the mantissa one position to the left, which is equivalent to multiplying by 2 frac <<= 1; // Keep the sign bit and reassemble the result to return return sign | frac; } exp += 1; // If the exponent becomes 255 after adding 1, it means it reaches infinity and returns infinity. if (exp == 255) { return sign | 0x7F800000; } // Retain the sign bit and new exponent and mantissa bits return sign | (exp << 23) | frac; } ``` 先提取出指數位的部分,觀察後可發現指數位加 1,就等於乘法 *2 並且透過指數位篩選掉 0、NaN、無窮大、無窮小 ### floatFloat2Int(f) ```c /* * floatFloat2Int - Return bit-level equivalent of expression (int) f * for floating point argument f. * Argument is passed as unsigned int, but * it is to be interpreted as the bit-level representation of a * single-precision floating point value. * Anything out of range (including NaN and infinity) should return * 0x80000000u. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 30 * Rating: 4 */ int floatFloat2Int(unsigned uf) { unsigned sign = uf >> 31; int exp = ((uf >> 23) & 0xFF) - 127; unsigned frac = (uf & 0x7FFFFF) | 0x800000; if ((uf & 0x7F800000) == 0x7F800000) { return 0x80000000u; } if (exp < 0) { return 0; } if (exp > 31) { return 0x80000000u; } if (exp > 23) { frac <<= (exp - 23); } else { frac >>= (23 - exp); } if (sign) { frac = -frac; } return frac; } ``` 過濾出符號位、指數位、尾數位,再去做特殊況處理 如果指數位全為 1,返回 0x80000000u。 若全為 0 直接返回 0。 單精度浮點數的偏置值是 127,因此實際指數為 exp - 127。 如果實際指數小於 0,則浮點數的小數部分在整數為 0 若大於 31,則超出 32 位有號整數的範圍,返回 0x80000000u。 尾數部分加上隱含的 1 位(即 1.xxxxx 變成 1xxxxx),如果指數大於等於 23,則將尾數左移,否則右移。 如果符號位為1,取負值。 ### floatPower2(x) ```c /* * floatPower2 - Return bit-level equivalent of the expression 2.0^x * (2.0 raised to the power x) for any 32-bit integer x. * * The unsigned value that is returned should have the identical bit * representation as the single-precision floating-point number 2.0^x. * If the result is too small to be represented as a denorm, return * 0. If too large, return +INF. * * Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while * Max ops: 31 * Rating: 4 */ unsigned floatPower2(int x) { int exp = x + 127; if (exp <= 0) return 0; if (exp >= 255) return 0x7f800000; return exp << 23; } ``` 單精度浮點數的指數偏置值是 127。 2 的 x 次方的指數表示為 x + 127。 如果 x + 127 小於 0,則結果太小,返回 0。 如果 x + 127 大於 255,則結果太大,返回正無窮大(+INF)。 指數位設置為 x + 127。 尾數位設置為 0,因為 2 的 x 次方的尾數部分為 1.0(隱含位)。 ### fitsShort? ```c /* * fitsShort - return 1 if x can be represented as a 16-bit, two's complement * integer. * Examples: fitsShort(33000) = 0, fitsShort(-32768) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 8 * Rating: 1 */ int fitsShort(int x) { // Shift x right by 15 bits to get the highest bit of x int sign = x >> 15; // Checks whether the most significant bit of x and x match the 16-bit two's // complement representation return !(sign ^ (x >> 15)); } ``` int sign = x >> 15;:將 x 右移 15 位,得到 x 的最高位(符號位)。 (x >> 15):得到 x 的符號位的複製。 sign ^ (x >> 15):如果 x 是正數,則sign 為0,結果為0;如果 x 是負數,則sign 為 -1,結果為 -1;如果 x 的符號位不一致,則結果為 -1。 !(sign ^ (x >> 15)):如果 x 的符號位元和 x 本身在合法範圍內,結果為 1;否則結果為 0。 ### floatAbsVal ```c /* * floatAbsVal - Return bit-level equivalent of absolute value of f for * floating point argument f. * Both the argument and result are passed as unsigned int's, * but they are to be interpreted as the bit-level * representations of single-precision floating point values. * When argument is NaN, return argument.. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 10 * Rating: 2 */ unsigned floatAbsVal(unsigned uf) { unsigned int n = *(unsigned int*)&uf; unsigned int mask = 0x7FFFFFFF; unsigned int abs_n = n & mask; return *((float*)&abs_n); } ``` ### floatInt2Float ```c /* * floatInt2Float - Return bit-level equivalent of expression (float) x * Result is returned as unsigned int, but it is to be * interpreted as the bit-level representation of a * single-precision floating point values. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 30 * Rating: 4 */ unsigned floatInt2Float(int x) { unsigned sign; unsigned frac; unsigned exp; unsigned u; if (x == 0) { return 0; } if (x < 0) { sign = 1u << 31; x = -x; } else { sign = 0; } frac = x; exp = 158; // 127 + 31 (bias) while (!(frac & 0x80000000)) { frac <<= 1; exp--; } u = sign | (exp << 23) | (frac & 0x7FFFFF); return u; } ``` sign:記錄 x 的符號位,如果 x 小於 0,則設定為 1,表示負數。 frac:初始化為 x 的絕對值,表示尾數。 exp:初始化為適當的指數值,透過不斷左移 frac 直到最高有效位元為 1 來調整。 u:使用 sign、exp 和 frac 建構最終的單精度浮點數位級表示。 ### floatIsEqual ```c /* * floatIsEqual - Compute f == g for floating point arguments f and g. * Both the arguments are passed as unsigned int's, but * they are to be interpreted as the bit-level representations * of single-precision floating point values. * If either argument is NaN, return 0. * +0 and -0 are considered equal. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 25 * Rating: 2 */ int floatIsEqual(unsigned uf, unsigned ug) { // Check for NaN if ((uf & 0x7FFFFFFF) > 0x7F800000 || (ug & 0x7FFFFFFF) > 0x7F800000) { return 0; } // Handle ±0 case if ((uf & 0x7FFFFFFF) == 0 && (ug & 0x7FFFFFFF) == 0) { return 1; } // Compare bit-level representations return uf == ug; } ``` NaN 檢測 如果 uf 或 ug 是 NaN(指數位全為1且尾數位非全0),則直接回傳0。 ±0 判定 若 uf 和 ug 的位級表示表示均為 ±0,即符號位元和尾數位元均為0,則傳回1。 一般情況比較 將 uf 和 ug 的位元級表示直接進行比較,如果完全相同則回傳1,否則回傳0。 ### floatIsLess ```c /* * floatIsLess - Compute f < g for floating point arguments f and g. * Both the arguments are passed as unsigned int's, but * they are to be interpreted as the bit-level representations * of single-precision floating point values. * If either argument is NaN, return 0. * +0 and -0 are considered equal. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 30 * Rating: 3 */ int floatIsLess(unsigned uf, unsigned ug) { // Check for NaN if ((uf & 0x7FFFFFFF) > 0x7F800000 || (ug & 0x7FFFFFFF) > 0x7F800000) { return 0; } // Handle ±0 case if ((uf & 0x7FFFFFFF) == 0 && (ug & 0x7FFFFFFF) == 0) { return 0; } // Compare sign bits int sign_f = uf >> 31; int sign_g = ug >> 31; if (sign_f != sign_g) { return sign_f < sign_g; } // Compare bit-level representations return uf < ug; } ``` NaN 檢測 如果 uf 或 ug 是 NaN(指數位全為1且尾數位非全0),則直接回傳0。 ±0 判定 如果 uf 和 ug 的位級表示表示均為 ±0,即符號位和尾數位均為0,則回傳0。 符號位比較 如果 uf 和 ug 的符號位不同,可以直接根據符號位來判斷大小關係。 一般情況比較 如果符號位相同,需要比較它們的位級表示來判斷大小關係。 ### floatNegate ```c /* * floatNegate - Return bit-level equivalent of expression -f for * floating point argument f. * Both the argument and result are passed as unsigned int's, * but they are to be interpreted as the bit-level * representations of single-precision floating point values. * When argument is NaN, return argument. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 10 * Rating: 2 */ unsigned floatNegate(unsigned uf) { // Check for NaN if ((uf & 0x7FFFFFFF) > 0x7F800000) { return uf; } // Toggle the sign bit return uf ^ 0x80000000; } ``` NaN 檢測:透過檢查指數位是否全為 1 且尾數位非全 0 來判斷是否為 NaN。 正負號位元取反:對於非 NaN 的情況,直接透過異或運算 uf ^ 0x80000000 來將正負號位元取反,從而得到 -f 的位級表示。 ### floatPower2 ```c /* * floatPower2 - Return bit-level equivalent of the expression 2.0^x * (2.0 raised to the power x) for any 32-bit integer x. * * The unsigned value that is returned should have the * identical bit representation as the single-precision * floating-point number 2.0^x. * If the result is too small to be represented as a denorm, * return 0. If too large, return +INF. * * Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while * Max ops: 30 * Rating: 4 */ unsigned floatPower2(int x) { // Check for overflow if (x > 127) { return 0x7F800000; // +INF } // Check for too small to be represented as denorm if (x < -126) { return 0; // Too small to be denorm } // Calculate exponent unsigned exp = 127 + x; // Construct the single-precision float representation return exp << 23; } ``` 特殊情況處理: 如果 x 大於 127,則傳回單精度浮點數的正無窮大表示(0x7F800000)。 如果 x 小於 -126,則傳回 0,因為此時 2.0^x 太小無法表示成規格化浮點數。 規格化浮點數處理: 對於處於規格化範圍內的 x,計算 2.0^x 的指數部分,並使用左移操作建立單精度浮點數的表示。 ### floatScale1d2 ```c /* * floatScale1d2 - Return bit-level equivalent of expression 0.5*f for * floating point argument f. * Both the argument and result are passed as unsigned int's, * but they are to be interpreted as the bit-level * representation of single-precision floating point values. * When argument is NaN, return argument * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 30 * Rating: 4 */ unsigned floatScale1d2(unsigned uf) { unsigned sign = uf & 0x80000000; unsigned exp = uf & 0x7F800000; unsigned frac = uf & 0x007FFFFF; // Check for NaN or infinity if (exp == 0x7F800000) { return uf; // Return uf if NaN or infinity } if (exp == 0) { // Denormalized case frac >>= 1; return sign | frac; } else { // Normalized case exp -= 0x00800000; // Subtract 1 from exponent to divide by 2 return sign | exp | frac; } } ``` 特殊情況處理: 如果參數 uf 是 NaN 或無限大(指數部分為 0x7F800000),直接回傳 uf。 規格化浮點數處理: 對於規格化浮點數,首先提取出符號位、指數位和尾數位。 如果是規格化浮點數,則需要將指數部分減去 0x00800000(相當於減去 1),以達到乘以 0.5 的效果。 將調整後的指數和原尾數重新組合成單精確度浮點數的位元級表示。 非規格化浮點數處理: 對於非規格化浮點數(指數部分為 0),直接將尾數右移一位即可達到乘以 0.5 的效果。 ### floatScale2 ```c /* * floatScale2 - Return bit-level equivalent of expression 2*f for * floating point argument f. * Both the argument and result are passed as unsigned int's, * but they are to be interpreted as the bit-level representation * of single-precision floating point values. * When argument is NaN, return argument * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while * Max ops: 30 * Rating: 4 */ unsigned floatScale2(unsigned uf) { unsigned sign = uf & 0x80000000; unsigned exp = uf & 0x7F800000; unsigned frac = uf & 0x007FFFFF; // Check for NaN or infinity if (exp == 0x7F800000) { return uf; // Return uf if NaN or infinity } if (exp == 0) { // Denormalized case frac <<= 1; return sign | frac; } else if (exp != 0x7F000000) { // Normalized case exp += 0x00800000; // Add 1 to exponent to multiply by 2 // Check for overflow if (exp >= 0x7F800000) { return 0x7F800000 | sign; // Return +INF } return sign | exp | frac; } else { // Handle the case when the exponent is already at the maximum value // (infinite result) return uf; } } ``` 特殊情況處理: 如果參數 uf 是 NaN 或無限大(指數部分為 0x7F800000),直接回傳 uf。 規格化浮點數處理: 對於規格化浮點數,首先提取出符號位、指數位和尾數位。 如果是規格化浮點數,需要將指數部分加上 0x00800000(相當於加上 1),以達到乘以 2 的效果。 如果乘以 2 後超過了單精確度浮點數的表示範圍(指數部分超過 0x7F800000),則傳回 +INF。 非規格化浮點數處理: 對於非規格化浮點數(指數部分為 0),直接將尾數左移一位即可達到乘以 2 的效果。 ### getByte ```c /* * getByte - Extract byte n from word x * Bytes numbered from 0 (least significant) to 3 (most significant) * Examples: getByte(0x12345678,1) = 0x56 * Legal ops: ! ~ & ^ | + << >> * Max ops: 6 * Rating: 2 */ int getByte(int x, int n) { return (x >> (n << 3)) & 0xFF; } ``` (n << 3):將 n 乘以 8 來計算位元組偏移量 x >> (n << 3):將 x 右移 (n << 3) 位,將位置 n 處的位元組帶到最低有效位元組位置。 & 0xFF:應用遮罩 (0xFF) 來提取最低有效位元組。 ### howManyBits ```c /* howManyBits - return the minimum number of bits required to represent x in * two's complement * Examples: howManyBits(12) = 5 * howManyBits(298) = 10 * howManyBits(-5) = 4 * howManyBits(0) = 1 * howManyBits(-1) = 1 * howManyBits(0x80000000) = 32 * Legal ops: ! ~ & ^ | + << >> * Max ops: 90 * Rating: 4 */ int howManyBits(int x) { int b16, b8, b4, b2, b1, b0; int sign = x >> 31; x = (sign & ~x) | (~sign & x); b16 = !!(x >> 16) << 4; x = x >> b16; b8 = !!(x >> 8) << 3; x = x >> b8; b4 = !!(x >> 4) << 2; x = x >> b4; b2 = !!(x >> 2) << 1; x = x >> b2; b1 = !!(x >> 1); x = x >> b1; b0 = x; return b16 + b8 + b4 + b2 + b1 + b0 + 1; } ``` 如果 x 為 0,則只需要 1 位。 如果 x 是 0x80000000,則需要 32 位元。 透過取其絕對值 (abs_x) 並考慮其符號 (sign_bit) 來標準化 x 。 使用二分查找法確定所需的最小位數: 逐漸檢查更大的位元大小,直到找到在 abs_x 中設定的最高位元。 這涉及到將 abs_x 右移 2 的冪並檢查結果數字是否非零。 根據abs_x中最高設定位的位置決定所需的位數。 如果 x 為負,則調整結果以考慮正負號號位元。 ### implication ```c /* * implication - return x -> y in propositional logic - 0 for false, * 1 for true * Example: implication(1, 1) = 1 * implication(1, 0) = 0 * Legal ops: ! ~ ^ | * Max ops: 5 * Rating: 2 */ int implication(int x, int y) { return !(x & !y); } ``` ### intLog2 ```c /* * intLog2 - return floor(log base 2 of x), where x > 0 * Example: intLog2(16) = 4 * Legal ops: ! ~ & ^ | + << >> * Max ops: 90 * Rating: 4 */ int intLog2(int x) { int result = 0; // Check if the higher 16 bits have any 1s result = !!(x >> 16) << 4; // if x >= 2^16, result = 16 result = result + (!!(x >> (result + 8)) << 3); // if x >= 2^(result+8), add 8 to result result = result + (!!(x >> (result + 4)) << 2); // if x >= 2^(result+4), add 4 to result result = result + (!!(x >> (result + 2)) << 1); // if x >= 2^(result+2), add 2 to result result = result + (!!(x >> (result + 1))); // if x >= 2^(result+1), add 1 to result return result; } ``` ### isEqual ```c /* * isEqual - return 1 if x == y, and 0 otherwise * Examples: isEqual(5,5) = 1, isEqual(4,5) = 0 * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ int isEqual(int x, int y) { return !(x ^ y); } ``` ### isLessOrEqual ```c /* * 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) { // Compute the sign bits of x and y int signX = x >> 31 & 1; int signY = y >> 31 & 1; // Case 1: x is negative, y is positive (x <= y is true) int xNegYPos = signX & ~signY; // Case 2: Both have the same sign int sameSign = !(signX ^ signY); // Calculate y - x int diff = y + (~x + 1); int diffSign = diff >> 31 & 1; // If sameSign is 1, we check the sign of the difference int lessOrEqualSameSign = sameSign & ~diffSign; // Combine the cases return xNegYPos | lessOrEqualSameSign; } ``` ### isNegative ```c /* * isNegative - return 1 if x < 0, return 0 otherwise * Example: isNegative(-1) = 1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 6 * Rating: 2 */ int isNegative(int x) { return !!(x >> 31); } ``` ### isNonNegative ```c /* * isNonNegative - return 1 if x >= 0, return 0 otherwise * Example: isNonNegative(-1) = 0. isNonNegative(0) = 1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 6 * Rating: 2 */ int isNonNegative(int x) { return !(x >> 31); } ``` ### isNonZero? ```c /* * isNonZero - Check whether x is nonzero using * the legal operators except ! * Examples: isNonZero(3) = 1, isNonZero(0) = 0 * Legal ops: ~ & ^ | + << >> * Max ops: 10 * Rating: 4 */ int isNonZero(int x) { int signMask = x >> 31; // Extract sign bit (all 1s if negative, 0 if non-negative) int negationShifted = (~x + 1) >> 31; // Negate x and shift, then extract sign bit return signMask | negationShifted; // Combine both results } ``` ### isNotEqual ```c /* * 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) { return !!(x ^ y); } ``` ### isPallindrome ```c /* * isPallindrome - Return 1 if bit pattern in x is equal to its mirror image * Example: isPallindrome(0x01234567E6AC2480) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 45 * Rating: 4 */ int isPallindrome(int x) { int mask = 0x1; int numBits = 32; // assuming x is 32-bit int mirroredX = 0; int originalX = x; // Build mirroredX from originalX for (int i = 0; i < numBits; ++i) { mirroredX = (mirroredX << 1) | (originalX & mask); originalX >>= 1; } // Compare originalX and mirroredX return !(mirroredX ^ x); } ``` 鏡像結構(mirroredX): 將 mirroredX 初始化為0。 迭代 originalX 的每一位: 提取 originalX 的最低有效位。 將 mirroredX 向左移動一位後,將此位元附加到 mirroredX。 將 originalX 右移一位以處理下一位。 比較 (!(mirroredX ^ x)): 使用 XOR (^) 將 MirroredX 與 x 進行比較。 如果 mirroredX 等於x,則表達式 mirroredX ^ x 將得到0。 應用邏輯非 (!) 將結果轉換為 1(如果相等)或 0(如果不相等)。 ### isPositive ```c /* * isPositive - return 1 if x > 0, return 0 otherwise * Example: isPositive(-1) = 0. * Legal ops: ! ~ & ^ | + << >> * Max ops: 8 * Rating: 2 */ int isPositive(int x) { return !(x >> 31); } ``` ### isPower2 ```c /* * isPower2 - returns 1 if x is a power of 2, and 0 otherwise * Examples: isPower2(5) = 0, isPower2(8) = 1, isPower2(0) = 0 * Note that no negative number is a power of 2. * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 4 */ int isPower2(int x) { // Check if x is greater than 0 and has exactly one bit set return (x > 0) && !(x & (x - 1)); } ``` (x & (x - 1)) == 0: 檢查 x 是否恰好有一位元為 1。按位與運算 x & (x - 1) 將得到 0。 (x > 0): 確保 x 為正數,因為負數不能是 2 的冪。 邏輯非(!): 如果 x 是 2 的冪,則將 (x & (x - 1)) == 0 && (x > 0) 的結果轉換為 1,否則轉換為 0。 ### isZero ```c /* * isZero - returns 1 if x == 0, and 0 otherwise * Examples: isZero(5) = 0, isZero(0) = 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 2 * Rating: 1 */ int isZero(int x) { return !x; } ``` ### leastBitPos ```c /* * leastBitPos - return a mask that marks the position of the * least significant 1 bit. If x == 0, return 0 * Example: leastBitPos(96) = 0x20 * Legal ops: ! ~ & ^ | + << >> * Max ops: 6 * Rating: 2 */ int leastBitPos(int x) { return x & (~x + 1); } ``` (~x + 1): ~x + 1 有效隔離 x 的最低有效 1 位元並將所有較低位元設為 0。 (x & (~x + 1)): 此操作會產生一個掩碼,其中僅 x 的最低有效 1 位元被設定為 (1),所有其他位元均為 0。 ### logicalShift ```c /* * logicalShift - shift x to the right by n, using a logical shift * Can assume that 0 <= n <= 31 * Examples: logicalShift(0x87654321,4) = 0x08765432 * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 3 */ int logicalShift(int x, int n) { // Arithmetic shift right by n int arith_shifted = x >> n; // Mask to clear the extra bits shifted in from the left int mask = ~(1 << 31 >> n << 1); // Combine with mask for logical shift effect return arith_shifted & mask; } ``` 1 << 31 建立僅設定正負號位元 (0x80000000) 的遮罩。 $>> n$ 將此遮罩右移 n 個位置,以與算術移位中移動的位置數對齊。 $<< 1$ 將其左移 1 個位置以建立一個遮罩,該遮罩清除算術移位期間從左移入的位元。 ~ 補充遮罩以準備按位 AND。 對算術移位結果套用遮罩(&mask)會清除從左移入的位元 ### maximumOfTwo ```c /* * maximumOfTwo - compute the maximum of two integers without branching * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 4 */ int maximumOfTwo(int x, int y) { // Calculate difference int diff = x - y; // Extract sign bit of diff int diff_sign = diff >> 31; // 0 if x >= y, 1 if x < y // Generate mask based on sign of diff int mask = diff_sign; // Return maximum of x and y return (x & ~mask) | (y & mask); } ``` ### minimumOfTwo ```c /* * minimumOfTwo - compute the minimum of two integers without branching * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 4 */ int minimumOfTwo(int x, int y) { // Calculate difference int diff = x - y; // Extract sign bit of diff int diff_sign = diff >> 31; // 0 if x <= y, 1 if x > y // Generate mask based on sign of diff int mask = diff_sign; // Return minimum of x and y return (x & ~mask) | (y & mask); } ``` ### minusOne ```c /* * minusOne - return a value of -1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 2 * Rating: 1 */ int minusOne(void) { return ~0; } ``` ### multFiveEighths ```c /* * multFiveEighths - multiplies by 5/8 rounding toward 0. * Should exactly duplicate effect of C expression (x*5/8), * including overflow behavior. * Examples: multFiveEighths(77) = 48 * multFiveEighths(-22) = -13 * multFiveEighths(1073741824) = 13421728 (overflow) * Legal ops: ! ~ & ^ | + << >> * Max ops: 12 * Rating: 3 */ int multFiveEighths(int x) { int mult5 = (x << 2) + x; // Equivalent to x * 5 int div8 = mult5 >> 3; // Equivalent to (x * 5) / 8 // Adjust for negative numbers to round towards zero int isNegative = x >> 31; // 0xFFFFFFFF if x is negative, 0 otherwise int adjust = isNegative & ((mult5 & 7) && 1); // Adjust only if x is negative and remainder is non-zero return div8 + adjust; } ``` (x << 2) + x 使用左移 (<<) 乘以 4,然後加上 x 來計算 x * 5。 mult5 >> 3 透過右移 (>> 3) 來計算 (x * 5) / 8,這相當於除以 8。 isNegative 計算 x 是否為負數(負數為 0xFFFFFFFF,否則為 0)。 adjustment 是僅當 x 為負 (isNegative) 且 x * 5 除以 8 (mult5 & 7) 的餘數非零 (&& 1) 時才應用的校正項。 ### oddBits ```c /* * oddBits - return word with all odd-numbered bits set to 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 8 * Rating: 2 */ int oddBits(void) { int mask = 0xAA; // Mask for odd-numbered bits: 10101010 int oddMask = mask | (mask << 8); // 10101010 | 10101010 << 8 = 1010101010101010 return oddMask | (oddMask << 16); // Combine with itself for 32 bits } ``` 遮罩為 0xAA,表示二進位模式 10101010,覆寫最低有效位元組。 oddMask 將該遮罩與自身左移 8 位元結合,產生 0xAAAA,覆蓋 32 位元結果的前 16 位元。 最後,oddMask 與自身組合左移 16 位元以覆蓋所有 32 位,得到 0xAAAAAAAA(二進位 10101010101010101010101010101010)。 oddMask 將該遮罩與自身左移 8 位元結合,產生 0xAAAA,覆蓋 32 位元結果的前 16 位元。 最後,oddMask 與其自身組合左移 16 位以覆蓋所有 32 位,得到 0xAAAAAAAA(二進位 10101010101010101010101010101010)。 ### remainderPower2 ```c /* * remainderPower2 - Compute x%(2^n), for 0 <= n <= 30 * Negative arguments should yield negative remainders * Examples: remainderPower2(15, 2) = 3, remainderPower2(-35, 3) = -3 * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 3 */ int remainderPower2(int x, int n) { int mask = (1 << n) - 1; // Mask with n lowest bits set to 1 int lowerBits = x & mask; // Extract the n lowest bits of x // Adjust for negative numbers int isNegative = x >> 31; // -1 if x is negative, 0 otherwise int adjust = isNegative & (1 << n); // Add 2^n if x is negative return lowerBits + adjust; } ``` ### replaceByte ```c /* * replaceByte(x,n,c) - Replace byte n in x with c * Bytes numbered from 0 (LSB) to 3 (MSB) * Examples: replaceByte(0x12345678, 1, 0xab) = 0x1234ab78 * You can assume 0 <= n <= 3 and 0 <= c <= 255 * Legal ops: ! ~ & ^ | + << >> * Max ops: 10 * Rating: 3 */ int replaceByte(int x, int n, int c) { int shift = n << 3; // shift = n * 8 int mask = ~(0xFF << shift); // mask to clear byte at position n int cleared_x = x & mask; // clear byte at position n in x int replacement = c << shift; // shift c into position n return cleared_x | replacement; // insert c into x } ``` shift = n << 3 計算到達 x 中的位元組 n 所需的位移位。 mask = ~(0xFF << shift) 建立一個掩碼,其中除了與位置 n 處的位元組相對應的位元外,所有位元均已設定。 Clear_x = x & mask 透過與遮罩的逆值進行「與」操作來清除 x 中位置 n 處的位元組。 replacement = c << shift 將位元組 c 移至位置 n。 返回clear_x | replacement 使用位元或將cleared_x(位置n處的位元組清除)和replacement(將位元組c插入位置n)組合起來。 ### rotateLeft ```c /* * rotateLeft - Rotate x to the left by n * Can assume that 0 <= n <= 31 * Examples: rotateLeft(0x87654321, 4) = 0x76543218 * Legal ops: ~ & ^ | + << >> ! * Max ops: 25 * Rating: 3 */ int rotateLeft(int x, int n) { int shift_mask = ~((~0) << n); // Mask to capture the bits that will rotate int rotated_bits = (x >> (32 - n)) & shift_mask; // Bits that will rotate to the leftmost side int result = (x << n) | rotated_bits; // Combine rotated bits with shifted x return result; } ``` shift_mask: ~((~0) << n) 建立一個遮罩,其中最右邊的 n 位元為 1,擷取將旋轉到最左側的位元。 rotate_mask: ~((~0) << (32 - n)) 建立一個遮罩,其中最左邊的 n 位為 1,捕獲將從最左側環繞到最右側的位元。 rotated_bits: (x >> (32 - n)) & shift_mask 透過將 x 右移 (32 - n) 個位置並應用 shift_mask 來隔離這些位,從而提取將旋轉到最左側的位。 結果:(x << n) | rotated_bits 透過將 x 向左移動 n 個位置來執行左旋轉,然後將其與rotated_bits 進行「或」(|) 運算,以將旋轉後的位元與移位後的 x 組合起來。 ### rotateRight ```c /* * rotateRight - Rotate x to the right by n * Can assume that 0 <= n <= 31 * Examples: rotateRight(0x87654321, 4) = 0x76543218 * Legal ops: ~ & ^ | + << >> ! * Max ops: 25 * Rating: 3 */ int rotateRight(int x, int n) { int rotate_mask = (~0) << (32 - n); // Mask to capture the bits that wrap around int rotated_bits = (x << (32 - n)) & rotate_mask; // Bits that will rotate to the rightmost side int result = (x >> n) | rotated_bits; // Combine rotated bits with shifted x return result; } ``` ### satAdd ```c /* * satAdd - adds two numbers but when positive overflow occurs, returns * maximum possible value, and when negative overflow occurs, * it returns minimum positive value. * Examples: satAdd(0x40000000, 0x40000000) = 0x7fffffff * satAdd(0x80000000, 0xffffffff) = 0x80000000 * Legal ops: ! ~ & ^ | + << >> * Max ops: 30 * Rating: 4 */ int satAdd(int x, int y) { int sum = x + y; int overflow = ((x ^ sum) & (y ^ sum)) >> 31; // Check if overflow happened // Calculate saturated result int saturated_result = (overflow & ((1 << 31) ^ x)) | (~overflow & sum); return saturated_result; } ``` sum:計算 x 和 y 的總和。 溢位:透過檢查 x、y 和 sum 的符號是否一致來偵測溢位。如果發生溢出,溢出將為 -1。 max_int 和 min_int:定義最大正值 (0x7fffffff) 和最小負值 (0x80000000) 的常數。 saturated_result:根據是否發生溢位計算結果。如果發生溢出(溢出為 -1),則根據 x 的符號選擇 max_int 或 min_int。如果沒有溢出,則傳回總和。 ### satMul2 ```c /* * satMul2 - multiplies by 2, saturating to Tmin or Tmax if overflow * Examples: satMul2(0x30000000) = 0x60000000 * satMul2(0x40000000) = 0x7FFFFFFF (saturate to TMax) * satMul2(0x80000001) = 0x80000000 (saturate to TMin) * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 3 */ int satMul2(int x) { int doubled = x << 1; // x * 2 int overflow = (x ^ doubled) >> 31; // Check if overflow happened // Calculate saturated result int saturated_result = (overflow & (x >> 31)) | (~overflow & doubled); return saturated_result; } ``` ### satMul3 ```c /* * satMul3 - multiplies by 3, saturating to Tmin or Tmax if overflow * Examples: satMul3(0x10000000) = 0x30000000 * satMul3(0x30000000) = 0x7FFFFFFF (Saturate to TMax) * satMul3(0x70000000) = 0x7FFFFFFF (Saturate to TMax) * satMul3(0xD0000000) = 0x80000000 (Saturate to TMin) * satMul3(0xA0000000) = 0x80000000 (Saturate to TMin) * Legal ops: ! ~ & ^ | + << >> * Max ops: 25 * Rating: 3 */ int satMul3(int x) { int tripled = (x << 1) + x; // x * 3 int overflow1 = (x ^ tripled) >> 31; // Check if overflow happened for positive x int overflow2 = tripled & (1 << 31); // Check if overflow happened for negative x // Calculate saturated result int saturated_result = (overflow1 & overflow2) | (~overflow1 & ~overflow2 & tripled); return saturated_result; } ``` ### sign ```c /* * sign - return 1 if positive, 0 if zero, and -1 if negative * Examples: sign(130) = 1 * sign(-23) = -1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 10 * Rating: 2 */ int sign(int x) { int sign_bit = x >> 31; // Get sign bit of x (0 for non-negative, 1 for negative) return sign_bit | (!!x); // Return sign_bit for negative or non-zero, and 1 // for positive } ``` x >> 31:將 x 右移 31 位,擷取符號位。如果 x 為非負 (sign_bit = 0),則此操作有效地將所有位元設為 0;如果 x 為負(sign_bit = -1,二進位補碼),則將所有位元設為 1。 !!x:如果 x 非零,則計算結果為 1;如果 x 為零,則計算結果為 0。這處理 x 為零的情況,確保函數在這種情況下傳回 0。 符號位 | (!!x):合併結果,對於正 x 回傳 1,對於負 x 回傳 -1,對於零 x 傳回 0。 ### signMag2TwosComp ```c /* * signMag2TwosComp - Convert from sign-magnitude to two's complement * where the MSB is the sign bit * Example: signMag2TwosComp(0x80000005) = -5. * Legal ops: ! ~ & ^ | + << >> * Max ops: 15 * Rating: 4 */ int signMag2TwosComp(int x) { int sign_bit = x >> 31; // Extract sign bit (0 for non-negative, 1 for negative) int mask = sign_bit << 31; // Create mask to fill with sign bits if negative return (x ^ mask) + (sign_bit & 1); // Convert to two's complement if negative } ``` sign_bit = x >> 31:將 x 右移 31 位元,隔離正負號位元。非負數為 0,負數為 1。 mask = sign_bit << 31:建立一個遮罩,如果 x 為非負數,則該遮罩為 0;如果 x 為負數,則該遮罩為 0x80000000(最高有效位集)。 x ^ mask:如果 x 為負,則 XOR 運算會翻轉 x 的所有位元(因為 mask 將為 0x80000000),將其轉換為二進位補數。 (sign_bit & 1):如果 x 為負數,則結果加 1,從而有效調整二進位補數表示形式。 ### specialBits ```c /* * specialBits - return bit pattern 0xffca3fff * Legal ops: ! ~ & ^ | + << >> * Max ops: 3 * Rating: 1 */ int specialBits(void) { return 0xffca3fff; } ``` ### subtractionOK ```c /* * subtractionOK - Determine if can compute x-y without overflow * Example: subtractionOK(0x80000000, 0x80000000) = 1, * subtractionOK(0x80000000, 0x70000000) = 0, * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 3 */ int subtractionOK(int x, int y) { int signX = (x >> 31) & 1; // Sign bit of x (0 for positive, 1 for negative) int signY = (y >> 31) & 1; // Sign bit of y (0 for positive, 1 for negative) int signResult = ((x - y) >> 31) & 1; // Sign bit of x - y // Check if x and y have different signs int differentSigns = signX ^ signY; // Check if the result sign differs from x's sign int overflow = differentSigns & (signX ^ signResult); // Return 1 if no overflow, 0 if overflow return !overflow; } ``` signX = (x >> 31) & 1:擷取 x 的符號位元。將 x 右移 31 位元,僅保留正負號位元。然後用 1 屏蔽以隔離符號位元。 signY = (y >> 31) & 1:與 signX 類似,提取 y 的符號位。 (x - y) >> 31:計算結果 x - y 的正負號位元。 signResult = ((x - y) >> 31) & 1:擷取 x - y 的正負號位元。 differentSigns =signX^signY:檢查 x 和 y 是否有不同的符號。 Overflow = differentSigns & (signX ^ signResult):根據 x、y 和 x - y 的符號檢查是否有溢出情況。 如果 x - y 可以在不溢出 (!overflow) 的情況下計算,則傳回 1;如果有溢出,則傳回 0。 ### thirdBits ```c /* * thirdBits - return word with every third bit (starting from the LSB) * set to 1 * Legal ops: ! ~ & ^ | + << >> * Max ops: 8 * Rating: 1 */ int thirdBits(void) { return 0'b0010 0100 1001 0010 0100 1001 0010 0100; } ``` ### TMax ```c /* * TMax - return maximum two's complement integer * Legal ops: ! ~ & ^ | + << >> * Max ops: 4 * Rating: 1 */ int tmax(void) { return ~(1 << 31); } ``` (1 << 31) 將二進位數 1 左移 31 位元。 對結果的每一位元取反。對於 0x80000000,位元 NOT 運算結果為 0x7FFFFFFF,這是二進位補數表示形式的最大正值 ### tmin ```c /* * tmin - return minimum two's complement integer * Legal ops: ! ~ & ^ | + << >> * Max ops: 4 * Rating: 1 */ int tmin(void) { return 1 << 31; } ``` ### trueFiveEighths ```c /* * trueFiveEighths - multiplies by 5/8 rounding toward 0, * avoiding errors due to overflow * Examples: trueFiveEighths(11) = 6 * trueFiveEighths(-9) = -5 * trueFiveEighths(0x30000000) = 0x1E000000 (no overflow) * Legal ops: ! ~ & ^ | + << >> * Max ops: 25 * Rating: 4 */ int trueFiveEighths(int x) { int mult5 = (x << 2) + x; // Compute 5 * x using (x << 2) + x int sign = mult5 >> 31; // Get the sign of mult5 (-1 if negative, 0 if non-negative) int div8 = mult5 >> 3; // Divide mult5 by 8 // Adjust div8 based on the sign of x return div8 + (sign & (x >> 31)); } ``` mult5 = (x << 2) + x 使用位移位有效地計算 5 * x,這透過使用加法和左移的屬性來避免溢位。 mult5 >> 31 提取 mult5 的正負號位元,如果 mult5 為負,則產生 -1 (0xFFFFFFFF);如果為非負,則產生 0 (0x00000000)。 除以 8:mult5 >> 3 將 mult5 右移 3 位,實現除以 8。 捨去調整:(sign & (x >> 31)) 根據 x 的符號調整結果。如果 x 為負(x >> 31 產生 -1),則此調整有效地將結果調整為朝零舍入。 ### trueThreeFourths ```c /* * trueThreeFourths - multiplies by 3/4 rounding toward 0, * avoiding errors due to overflow * Examples: trueThreeFourths(11) = 8 * trueThreeFourths(-9) = -6 * trueThreeFourths(1073741824) = 805306368 (no overflow) * Legal ops: ! ~ & ^ | + << >> * Max ops: 20 * Rating: 4 */ int trueThreeFourths(int x) { int mult3 = (x << 1) + x; // Compute 3 * x using (x << 1) + x int sign = mult3 >> 31; // Get the sign of mult3 (-1 if negative, 0 if non-negative) int div4 = mult3 >> 2; // Divide mult3 by 4 // Adjust div4 based on the sign of x return div4 + (sign & (x >> 31)); } ``` 乘以 3:(x << 1) + x 使用位移位有效地計算 3 * x,這透過使用加法和左移的屬性來避免溢位。 mult3 >> 31 提取 mult3 的正負號位元,如果 mult3 為負,則產生 -1 (0xFFFFFFFF);如果為非負,則產生 0 (0x00000000)。 除以 4:mult3 >> 2 將 mult3 右移 2 位,實現除以 4。 捨去調整:(sign & (x >> 31)) 根據 x 的符號調整結果。如果 x 為負(x >> 31 產生 -1),則此調整有效地將結果調整為朝零舍入。 ### twosComp2SignMag ```c /* * twosComp2SignMag - Convert from two's complement to sign-magnitude * where the MSB is the sign bit * You can assume that x > TMin * Example: twosComp2SignMag(-5) = 0x80000005. * Legal ops: ! ~ & ^ | + << >> * Max ops: 15 * Rating: 4 */ int twosComp2SignMag(int x) { int sign = x >> 31; // Get sign of x (0 for non-negative, -1 for negative) int absX = (x ^ sign) + (~sign + 1); // Absolute value of x // If x is negative, convert to sign-magnitude return (sign & 0x80000000) | absX; } ``` 提取正負號位元:x >> 31 提取 x 的正負號位元。對於負 x,符號將為 -1 (0xFFFFFFFF),對於非負 x,符號將為 0 (0x00000000)。 絕對值計算: 如果 x 為負數(符號 = -1),x ^ 符號會切換 x 的所有位,從而有效地計算二進位補碼中的 -x - 1。 (~sign + 1) 將反轉後的值加 1,以獲得 x 的絕對值。 構造符號幅度表示: 如果 x 為負,(sign & 0x80000000) 將符號位元設為 0x80000000。 | absX 將符號位元與絕對值組合起來形成 x 的符號-數值表示。 ### upperBits ```c /* * upperBits - pads n upper bits with 1's * You may assume 0 <= n <= 32 * Example: upperBits(4) = 0xF0000000 * Legal ops: ! ~ & ^ | + << >> * Max ops: 10 * Rating: 1 */ int upperBits(int n) { int mask = !!n << 31; // If n == 0, mask will be 0x00000000; otherwise, it // will be 0x80000000 mask >>= (n + ~0); // Shift mask to position 32-n bits of 1 at MSB return mask; } ``` !!n:這會將 n 轉換為布林值(如果 n 為 0,則為 0,否則為 1)。這用於建立一個遮罩,如果 n 為零,則該遮罩以所有位元 0 開頭;如果 n 不為零,則所有位元都設為開頭。 << 31:將布林結果左移 31 位元,如果 n 非零,則結果為 0x80000000;如果 n 為零,則結果為 0x00000000。 $>> (n + ~0)$:將遮罩右移 (32 - n) 個位置。當 n 為 0 時,這會導致所有位元向右移動 32 個位置,從而有效地將遮罩清除為 0x00000000。對於非零 n,這會將 1 位元移動到較高的 n 個位置,從而建立所需的遮罩。 :::danger 注意書寫規範: * 無論標題和內文中,**中文和英文字元之間要有空白字元** (對排版和文字搜尋有利) * 留意科技詞彙的使用,請參見「[資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary)」和「[詞彙對照表](https://hackmd.io/@l10n-tw/glossaries)」 * 不該出現任何簡體中文字,並使用本地詞彙 * 程式碼註解不該出現中文,總是用美式英語書寫 * 用流暢的漢語書寫 * 使用 lab0 規範的程式碼書寫風格,務必用 clang-format 確認一致 * 在中文敘述中,使用全形標點符號,例如該用「,」,而非 "," ::: ## TODO: 從 Data Lab 選出可對應到真實應用的案例 > 最好在 Linux 核心找出相關程式碼,解說應搭配有效的測試程式碼 ### float_to_long ```c /** * float_to_long - Convert ieee754 reading from hardware to an integer * * @data: Value read from the hardware * @precision: Units to multiply up to eg. 1000 = milli, 1000000 = micro * * Return: Converted integer reading * * Depending on the measurement type the hardware returns an ieee754 * floating point value in either volts, amps or celsius. This function * will convert that into an integer in a smaller unit such as micro-amps * or milli-celsius. The hardware does not return NaN, so consideration of * that is not required. */ static long float_to_long(u32 data, u32 precision) { u64 man = data & 0x007FFFFF; int exp = ((data & 0x7F800000) >> 23) - 127 - 23; bool negative = data & 0x80000000; long result; man = (man + (1 << 23)) * precision; if (fls64(man) + exp > (int)sizeof(long) * 8 - 1) result = LONG_MAX; else if (exp < 0) result = (man + (1ull << (-exp - 1))) >> -exp; else result = man << exp; return negative ? -result : result; } ``` 遇到浮點數問題通常都會用到這 3 種遮罩 0x007FFFFF 過濾出尾數部分 0x7F800000 過濾出指數部分 0x80000000 過濾出正負號位元 ### parity check ```C static unsigned int mtdswap_test_patt(unsigned int i) { return i % 2 ? 0x55555555 : 0xAAAAAAAA; } ``` 通常用在傳輸位元組的錯誤檢測 函數根據輸入參數 i 的奇偶性返回兩個固定模式之一: 當 i 是奇數時: i % 2 結果為 1,返回值為 0x55555555。 0x55555555 在二進位制中為 01010101010101010101010101010101。 當 i 是偶數時: i % 2 結果為 0,返回值為 0xAAAAAAAA。 0xAAAAAAAA 在二進位制中為 10101010101010101010101010101010。 ### clk_zero ```c static void dsi_dphy_timing_calc_clk_zero(struct msm_dsi_dphy_timing *timing, s32 ui, s32 coeff, s32 pcnt) { s32 tmax, tmin, clk_z; s32 temp; /* reset */ temp = 300 * coeff - ((timing->clk_prepare >> 1) + 1) * 2 * ui; tmin = S_DIV_ROUND_UP(temp, ui) - 2; if (tmin > 255) { tmax = 511; clk_z = linear_inter(2 * tmin, tmin, pcnt, 0, true); } else { tmax = 255; clk_z = linear_inter(tmax, tmin, pcnt, 0, true); } /* adjust */ temp = (timing->hs_rqst + timing->clk_prepare + clk_z) & 0x7; timing->clk_zero = clk_z + 8 - temp; } ``` tmax: 根據 tmin 的值來設定,如果 tmin 大於255,則 tmax 設為511,否則設為255。 ### read_reg ```c int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) { struct max8998_dev *max8998 = i2c_get_clientdata(i2c); int ret; mutex_lock(&max8998->iolock); ret = i2c_smbus_read_byte_data(i2c, reg); mutex_unlock(&max8998->iolock); if (ret < 0) return ret; ret &= 0xff; *dest = ret; return 0; } ``` 這裡的 0xff 確保讀取的數據只保留低 8 位元 ### reverse_bytes ```c static __u32 reverse_bytes(__u32 b, int len) { switch (len) { case 32: b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16); fallthrough; case 16: b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8); fallthrough; case 8: b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4); fallthrough; case 4: b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2); fallthrough; case 2: b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); case 1: case 0: break; default: printk(KERN_ERR "DBRI reverse_bytes: unsupported length\n"); } return b; } ``` 指定的位元長度 len,使用多種位元操作來反轉整數 b 的位元順序。每個 case 語句後的 fallthrough 允許繼續執行下一個 case, ### machine_restart ```c static void machine_restart(char *command) { local_irq_disable(); /* reboot magic */ ltq_w32(BOOT_PW1, (void *)BOOT_PW1_REG); /* 'LTQ\0' */ ltq_w32(BOOT_PW2, (void *)BOOT_PW2_REG); /* '\0QTL' */ ltq_w32(0, (void *)BOOT_REG_BASE); /* reset Bootreg RVEC */ /* watchdog magic */ ltq_w32(WDT_PW1, (void *)WDT_REG_BASE); ltq_w32(WDT_PW2 | (0x3 << 26) | /* PWL */ (0x2 << 24) | /* CLKDIV */ (0x1 << 31) | /* enable */ (1), /* reload */ (void *)WDT_REG_BASE); unreachable(); } ``` 當系統出現故障或陷入無限循環時,看門狗定時器可以強制重啟系統。設置這個位元確保了看門狗定時器的啟用,使其能夠在必要時觸發系統重啟。 ### hsync_polarity_show ```c static ssize_t hsync_polarity_show(struct device *dev, struct device_attribute *attr, char *buf) { struct video_device *vdev = to_video_device(dev); struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); u32 config = mgb4_read_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync); return sprintf(buf, "%u\n", (config & (1U << 31)) >> 31); } ``` 提取HSYNC配置中的正負號位元(第31位) ## CS:APP 閱讀筆記 三種最重要數值表示方式 1.無號數 (unsigned) 2.二補數 (two's-complement) 3.浮點數 (floating-point) :::danger 注意用語: * bit 的翻譯是「位元」,而非「位」,後者是量詞 * compatible 是「相容」 * signed integer 是「有號整數」,而非「有符號」 * byte 是「位元組」,而非「字節」,抖音不要看太多 * 務必使用本課程教材的詞彙和慣例 ::: int 正整數,遇到 overflow 會產生溢出,從而變負數 浮點數,遇到 overflow 會產出特殊值 +ini 32 bits = 4 GB 64 bits = 16 GB 通常 64 位元可以相容 32 位元機器編譯的程式 :::danger 注意書寫規範: * 無論標題和內文中,**中文和英文字元之間要有空白字元** (對排版和文字搜尋有利) * 留意科技詞彙的使用,請參見「[資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary)」和「[詞彙對照表](https://hackmd.io/@l10n-tw/glossaries)」 * 不該出現任何簡體中文字,並使用本地詞彙 * 程式碼註解不該出現中文,總是用美式英語書寫 * 用流暢的漢語書寫 * 使用 lab0 規範的程式碼書寫風格,務必用 clang-format 確認一致 * 在中文敘述中,使用全形標點符號,例如該用「,」,而非 "," ::: | 有號 | 無號 | 32 位元<s>字節數</s> 位元組數量 | 64 位元組數量 | | -------- | -------- | -------- |----| |char|unsigned char| 1 |1 | |short|unsigned short| 2 |2 | |int|unsigned int| 4 |4 | |long|unsigned long| 4 |8 | |int32_t|uint32_t| 4 |4 | |int64_t|uint64_t| 8 |8 | |`char *`|| 4 |8 | |float|| 4 |4 | |double|| 8 |8 | * ISO C99 引入 int32_t,int64_t,其特性是位元組<s>字節</s> 大小固定,不隨編譯器和機器設置而變化 ### 練習 2.10 :::danger 注意書寫規範: * 無論標題和內文中,漢字和英語字元之間要有空白字元 (對排版和文字搜尋有利) * 使用 lab0 規範的程式碼書寫風格,務必用 clang-format 確認一致 ::: ```c void inplace_swap(int *x, int *y) { *x = *x ^ *y; // step1 *y = *x ^ *y; // step2 *x = *x ^ *y; // step3 } ``` 透過 xor 運算達成數值交換。 位移運算與優先級問題 * 加法(和減法)優先級比移位運算高 * 由左至右結合性規則 :::danger 查閱 C 語言規格書,摘錄運算子優先權相關章節。 ::: | 符號 | 運算的類型 | 關聯性 | | ---- | -------------------- | ---------- | | [ ] ( ) . -> ++-- (後置) | 運算式 | 由左至右 | | sizeof & * + - ~ ! ++-- (前置) | 一元 | 由右至左 | | 類型轉換 | 一元 | 由右至左 | | * / % | 乘法 | 由左至右 | | + - | 加法 | 由左至右 | | << >> | 位元移位 | 由左至右 | | < > <= >= | 關聯式 | 由左至右 | | == != | 等式 | 由左至右 | | & | 位元 AND | 由左至右 | | ^ | 位元互斥 OR | 由左至右 | | \ | 位元包含 OR | 由左至右 | | && | 邏輯 AND | 由左至右 | | \\ | 邏輯 OR | 由左至右 | | ? : | 條件運算式 | 由右至左 | | = *= /= %= += -= <<= >>= &= ^= |= | 簡單和複合指派 | 由右至左 | | , | 循序求值 | 由左至右 | ## 閱讀其他學員期末專題開發紀錄並提出建議 [井字遊戲模組-Willsonbo](https://hackmd.io/@sysprog/BkIOuG2VA) [重作第四次作業-Ken-LuWeiRu](https://hackmd.io/ilF4J8G5SZ2D2OHGNcCDeQ) [改進 ksort-yc199911](https://hackmd.io/@sysprog/SkjrHaxP0) [改進 ksort-tintinjian12999](https://hackmd.io/@sysprog/BkqsnDc8R) [改進 ttt 作為核心模組](https://hackmd.io/M5-ijGWrRLOgs2lHBtkTYw)