# 2018q3 Homework5 (bits) contributed by < `happyincent` > ## Environment ```bash $ cat <(echo "CPU: " `lscpu | grep "Model name" | cut -d':' -f2 | sed "s/ //"`) <(echo "OS: " `lsb_release -d | cut -f2`) <(echo "Kernel: " `uname -a | cut -d' ' -f1,3,14`) <(echo "gcc: " `gcc --version | head -n1`) CPU: Intel(R) Xeon(R) CPU E5520 @ 2.27GHz OS: Ubuntu 16.04.5 LTS Kernel: Linux 4.15.0-38-generic x86_64 gcc: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609 ``` #### Install `gcc-multilib` ```bash $ echo "int main(){}" | gcc -xc -m32 - &>/dev/null; echo $? 1 $ sudo apt install gcc-multilib ... $ echo "int main(){}" | gcc -xc -m32 - &>/dev/null; echo $? 0 ``` ## bit.c ### 1. satAdd > 確認 x+y 是否 overflow,並在發生 (+/-) overflow 時 retuen 最大 or 最小整數 * 取出 x, y, x+y 的 sign bit,在 `正+正=負` 或 `負+負=正` 時發生 overflow * 最小整數 = `0x80 << 24`,最大整數: `最小整數 - 1` ```c int satAdd(int x, int y) { int x1 = (x >> 31) & 0x1; int y1 = (y >> 31) & 0x1; int test = x + y; int t1 = (test >> 31) & 0x1; // 0 0 0 // 0 0 1 V // 0 1 0 // 0 1 1 // 1 0 0 // 1 0 1 // 1 1 0 V // 1 1 1 unsigned min = 0x80 << 24; unsigned max = min - 1; unsigned o1 = x1 & y1 & !t1; unsigned o2 = !x1 & !y1 & t1; unsigned check = o1 | o2; return ((((~(!o1) + 1) & max) | ((~(!o2) + 1) & min)) & (~(check) + 1)) | ((~(!check) + 1) & test); } ``` ### 2. bang > 計算 !x * 位移後做 or 運算,只要有其中一個 bit 為 1,就會得到 1 並 return `~x & 0x1` 得到 0 ```c int bang(int x) { unsigned mask = (0xff << 8) + 0xff; x = (x >> 16) | (x & mask); x |= x >> 8; x |= x >> 4; x |= x >> 2; x |= x >> 1; return ~x & 0x1; } ``` ### 3. byteSwap > 對調 x 的第 n, m 個 byte > Example: byteSwap(0xDEADBEEF, 0, 2) = 0xDEEFBEAD * 以 mask 取出不變的 byte,e.g. * n=1, m=3, mask = `00ff00ff` * 取出第 n byte `0x000000EF` 右移 n byte 至第 0 byte,左移 m byte 到欲交換的第 m byte `0x00ef0000` * 轉 unsigned 做 zero extension 的右移 > GNU GCC 3.8 - [Bit Shifting](https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Bit-Shifting) > C99 - 6.5.7 - 5 > The result of E1 >> E2 is E1 right-shifted E2 bit positions. > If E1 has an **unsigned type** or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2^E2^. * 對第 m byte 重複上述操作 ```c int byteSwap(int x, int n, int m) { unsigned y = x; unsigned mask = ~((0xff << (n << 3)) | (0xff << (m << 3))); return ((y & mask) | ((y & (0xff << (n << 3))) >> (n << 3) << (m << 3)) | ((y & (0xff << (m << 3))) >> (m << 3) << (n << 3))); } ``` ### 4. fitsBits > 判斷 x 是否可以 n-bit two's complement 表示 ![](https://i.imgur.com/G5YyGYW.png) [[source](https://en.wikipedia.org/wiki/Two%27s_complement)] * 由上圖可以看出最小 n-bit 的 2's complement 整數 * n=1, 111, 第 0 個 bit 開始為 1 * n=2, 110, 第 1 個 bit 開始為 1 * n=3, 100, 第 2 個 bit 開始為 1 * 因此在最小負數情況下 * `x >> 31` 後為 `0xffffffff` * 將 x 右移 `n - 1` 個 bit 後為 `0xffffffff` * XOR 兩者後得到 `0x00000000` * 在最大正數情況下,結果一樣 * `0x00000000` ^ `0x00000000` = `0x00000000` ```c int fitsBits(int x, int n) { // 1 : 0 ~ -1 // 2 : 1 ~ -2 return !((x >> 31) ^ (x >> (n - 1))); } ``` ### 5. isAsciiDigit > 判斷是否屬於 ASCII 的數字 '0' 到 '9' > (return 1 if 0x30 <= x <= 0x39) * 條件 * `x - 0x3A` < 0 * `x - 0x30` ≥ 0 ```c int isAsciiDigit(int x) { return ((x + (~(0x3A) + 1)) >> 31) & (~((x + (~(0x30) + 1)) >> 31) & 0x1); } ``` ### 6. rotateRight > 向右輪轉 n (`0 <= n <= 31`) 個 bit > Examples: rotateRight(0x87654321, 4) = 0x18765432 > (題目 Example 有錯) * 以 mask 取出要 rotate 的 bit,e.g. * n=4, mask = `00000010 - 1` = `0000000f` * 左移的部分 `<< (32 - n)`,右移的部分 `>> n` ```c int rotateRight(int x, int n) { unsigned mask = ((0x1 << n) + ((~1) + 1)); return ((x & mask) << (32 + (~n +1))) | ((x & (~mask)) >> n); } ``` ### 7. upperBits > 將 n 個 upper bits 填上 1 * `!!n & 0x1` * 0 -> 0x0 * 非 0 -> 0x1 * [wiki](https://en.wikipedia.org/wiki/Two%27s_complement) 的 2's complement 舉例: > The two's complement of an N-bit number is defined as its complement with respect to 2^N^. > For instance, for the three-bit number 010, the two's complement is 110, because 010 + 110 = 1000. * 將 1 左移 `32 - n` bit 再做 2's complement 運算後即可產生前 n 個 bit 為 1 的結果 ```c int upperBits(int n) { int x = ((!!n & 0x1) << (32 + (~n +1))); return (~x + 1); } ```