# 2018q3 Homework5 (bits) contributed by < `plusline` > ### 環境設定 ``` plusline@plusline-System-Product-Name:~/plusline/datalab$ make gcc -O0 -g -Wall -Werror -m32 -lm -o btest bits.c btest.c decl.c tests.c /usr/lib/gcc/i686-linux-gnu/7/../../../../i686-linux-gnu/bin/ld: /usr/lib/gcc/i686-linux-gnu/7/liblto_plugin.so: error loading plugin: /usr/lib/gcc/i686-linux-gnu/7/liblto_plugin.so: wrong ELF class: ELFCLASS32 collect2: error: ld returned 1 exit status Makefile:13: recipe for target 'btest' failed make: *** [btest] Error 1 ``` 正在尋找原因...似乎是與 64-bit 和 32-bit 有關,已經安裝 ``` $ sudo apt update $ sudo apt install libc6-dev:i386 gcc:i386 ``` 仍然出現問題 solution:[HowTo Compile a 32-bit Application Using gcc On the 64-bit Linux Version](https://www.cyberciti.biz/tips/compile-32bit-application-using-gcc-64-bit-linux.html) 測試 ``` plusline@plusline-System-Product-Name:~/plusline/datalab$ make ``` ``` Total points: 0/228 ``` :::info 注意上面的訊息提到不相容的 LTO gcc plugin,可找手冊來關閉 plugin,再來測試 :notes: jserv ::: :::info 根據上面的作法已經解決問題了,但還是想問老師說的手冊是什麼?是指 man gcc 嗎? by plusline ::: > man 就是 manual 的簡稱,手冊是也 :notes: jserv > 可是有一萬五千多行...  by plusline ### 修改 bits.c #### absVal ```c int absVal(int x) { int y = x >> 31; return (y ^ x) - y; } ``` 不能用`-`所以改成 ```c int absVal(int x) { int y = x >> 31; return (y ^ x) + (~y+1); } ``` 如果沒有先拿出來考過真很難想到不用 if 怎麼寫。 拿到四分,不過無法 commit 上去, error 的提示讓人覺得很奇怪,不是只要 shift 小於等於31就有定義嗎? ``` plusline@plusline-System-Product-Name:~/plusline/datalab$ git commit -a -m 'Complete absVal' [bits.c:114]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:92]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:110]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:112]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:132]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:141]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:407]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:436]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:551]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour [tests.c:737]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour Fail to pass static analysis. ``` > 注意有號數的使用,應善用 `<stdint.h>` > [name="jserv"] :::info 分成兩次 shift 就能取消 error ,剩下的錯誤都發生在 test.c 我應該去改嗎? ::: > 改吧,順便提交 pull request > [name="jserv"] #### addOK 正數加上正數應該產生正數,負數加上負數應該產生負數。本來想和硬體一樣用最後兩個位元做互斥或,只是不知道怎得到進位。。 ```c int addOK(int x, int y) { int sum = x + y; int same = (~(x ^ y)) & (sum ^ x); return ((~same) >> (32 - 1)) & 1; } ``` #### allEvenBits 把長條的 bits 想像成棉被,對折對折再對折。 ```c int allEvenBits(int x) { x=x&(x>>2); x=x&(x>>4); x=x&(x>>8); x=x&(x>>16); x=x&1; return x; } ``` #### allOddBits 作法和 allEvenBits 差不多,只是最後要看的是最小第二位。 ```c int allOddBits(int x) { x = x & (x >> 2); x = x & (x >> 4); x = x & (x >> 8); x = x & (x >> 16); x = x >> 1; x = x & 1; return x; } ``` #### anyEvenBit ```c int anyEvenBit(int x) { x = x | (x >> 2); x = x | (x >> 4); x = x | (x >> 8); x = x | (x >> 16); x = x & 1; return x; } ``` #### anyOddBit ```c int anyOddBit(int x) { x = x | (x >> 2); x = x | (x >> 4); x = x | (x >> 8); x = x | (x >> 16); x = x >> 1; x = x & 1; return x; } ``` #### bang ```c int bang(int x) { x = x | (x >> 1); x = x | (x >> 2); x = x | (x >> 4); x = x | (x >> 8); x = x | (x >> 16); x = ~x & 1; return x; } ``` #### bitAnd 用笛摩根定理 ```c int bitAnd(int x, int y) { return ~((~x) | (~y)); } ``` #### bitCount 版本一 很明顯超過40個運算子,正在思考如何簡化。 目前的方式是畫真值表,再做成半加器的形式。 ```c //***** int bitCount(int x) { int A=x; int B=x>>1; int S=A^B;//count even number int C=A&B;//count even number and shift left one bit //***** A=S; B=S>>2; int S1_1=A^B; int C1_1=A&B;//shift one A=C; B=C>>2; int S1_2=A^B;//shift one int C1_2=A&B;//shift two //***** A=S1_1; B=S1_1>>4; int S2_1=A^B;//zero int C2_1=A&B;//one A=C1_1; B=C1_1>>4; int S2_2=A^B;//one int C2_2=A&B;//two A=S1_2; B=S1_2>>4; int S2_3=A^B;//one int C2_3=A&B;//two A=C1_2; B=C1_2>>4; int S2_4=A^B;//two int C2_4=A&B;//three //***** int h=1+(1<<8)+(1<<16)+(1<<24); int a1=S2_1&h; int a2=C2_1&h; int a3=S2_2&h; int a4=C2_2&h; int a5=S2_3&h; int a6=C2_3&h; int a7=S2_4&h; int a8=C2_4&h; int sum=a1+(a2<<1)+(a3<<1)+(a4<<2)+(a5<<1)+(a6<<2)+(a7<<2)+(a8<<3); int ans=sum+(sum>>8); ans=ans+(ans>>16); ans=ans&((1<<6)+(~1+1)); return ans; } ``` 版本二 已經少用很多運算子了,但還是多了9個,相較上一版本,少做一層半加的動作。 ```c int bitCount(int x) { int A = x; int B = x >> 1; int S = A ^ B; // count even number int C = A & B; // count even number and shift left one bit //***** A = S; B = S >> 2; int S1_1 = A ^ B; int C1_1 = A & B; // shift one A = C; B = C >> 2; int S1_2 = A ^ B; // shift one int C1_2 = A & B; // shift two //***** int h = 15 + (15 << 8) + (15 << 16) + (15 << 24); int hh=1+(1<<4)+(1<<8)+(1<<12)+(1<<16)+(1<<20)+(1<<24)+(1<<28); int aa1=S1_1&hh; int aa2=C1_1&hh; int aa3=S1_2&hh; int aa4=C1_2&hh; int Sum=aa1+(aa2<<1)+(aa3<<1)+(aa4<<2); Sum=Sum+(Sum>>4); Sum=Sum&h; Sum=Sum+(Sum>>8); Sum = Sum + (Sum >> 16); Sum = Sum & ((1 << 6) + (~1 + 1)); return Sum; } ``` 版本三 成功將運算子壓到39個,相較上一版本,化簡 mask 。 ```c int bitCount(int x) { int A = x; int B = x >> 1; int S = A ^ B; // count even number int C = A & B; // count even number and shift left one bit //***** A = S; B = S >> 2; int S1_1 = A ^ B; int C1_1 = A & B; // shift one A = C; B = C >> 2; int S1_2 = A ^ B; // shift one int C1_2 = A & B; // shift two //***** int d=15+(15<<16); int dd=d+(d<<8); int k=1+(1<<16); int kk=k+(k<<8); int kkk=kk+(kk<<4); int aa1 = S1_1 & kkk; int aa2 = C1_1 & kkk; int aa3 = S1_2 & kkk; int aa4 = C1_2 & kkk; int Sum = aa1 + (aa2 << 1) + (aa3 << 1) + (aa4 << 2); Sum = Sum + (Sum >> 4); Sum = Sum & dd; Sum = Sum + (Sum >> 8); Sum = Sum + (Sum >> 16); Sum = Sum & ((31<<1)+1); return Sum; } ``` #### bitMask 分別將低於 highbit 的位元設為1,低於 lowbit 設為1,再取交集。 ```c int bitMask(int highbit, int lowbit) { int h = (~1 + 1) << 1; h = h << highbit; h = ~h; int l = 1 << (lowbit); l = l + (~1 + 1); int mask = h & ~l; return mask; } ``` #### bitMatch 提示讀半天,原來是要做 equal 或是 NXOR 。 ```c int bitMatch(int x, int y) { return (x&y)|(~x&~y); } ``` #### bitNor 用笛摩根定理 ```c int bitNor(int x, int y) { return (~x)&(~y); } ``` #### bitOr 上一題加 not 。 ```c int bitOr(int x, int y) { return ~((~x)&(~y)); } ``` #### bitParity 第一個禮拜的題目,相信大家都把答案背下來了 XD 。 ```c int bitParity(int x) { int sum=x^(x>>1); sum=sum^(sum>>2); sum=sum^(sum>>4); sum=sum^(sum>>8); sum=sum^(sum>>16); sum=sum&1; return sum; } ``` #### bitReverse 相鄰交換,隔一個,隔兩個,隔四個,隔八個,隔十六個交換,順序沒差。 ```c int bitReverse(int x) { int h1 = (1 << 16) + (~1 + 1); // 0x0000ffff int h2 = (h1 >> 8); // 0x00ff00ff h2 = h2 + (h2 << 16); int h3 = (h2 >> 4) & h2; // 0x0f0f0f0f h3 = h3 + (h3 << 8); int h4 = (h3 >> 2) & h3; // 0x33333333 h4 = h4 + (h4 << 4); int h5 = (h4 >> 1) & h4; // 0x55555555 h5 = h5 + (h5 << 2); x = ((x >> 1) & h5) | ((x & h5) << 1); x = ((x >> 2) & h4) | ((x & h4) << 2); x = ((x >> 4) & h3) | ((x & h3) << 4); x = ((x >> 8) & h2) | ((x & h2) << 8); x = ((x >> 16) & h1) | ((x & h1) << 16); return x; } ``` #### bitXor 畫真值表 | x | y | return | | -------- | -------- | -------- | | 0 | 0 | 0 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 1 | 1 | 0 | ```c int bitXor(int x, int y) { return (~x & y) | (x & ~y); } ``` #### byteSwap 取出要交換的部份,直接交換。 ```c int byteSwap(int x, int n, int m) { int mask = 15; mask = mask + (mask << 4); int shift_n = n << 3; int shift_m = m << 3; int mask_n = mask << shift_n; int mask_m = mask << shift_m; int get_n = mask_n & x; int get_m = mask_m & x; int change_n = ((get_n >> shift_n) & mask) << shift_m; int change_m = ((get_m >> shift_m) & mask) << shift_n; int ans = (x & ~mask_n & ~mask_m) | change_n | change_m; return ans; } ``` #### conditional 用 shannon expansion 。 ```c int conditional(int x, int y, int z) { int i = x | (x << 1); i = i | (i << 2); i = i | (i << 4); i = i | (i << 8); i = i | (i << 16); i = i >> 30 >> 1; return (i & y) | (~i & z); } ``` #### countLeadingZero 把所有一右邊的位元全部補成一,再數有幾個零。 ```c int countLeadingZero(int x) { int i = x; i = i | (i >> 16); i = i | (i >> 8); i = i | (i >> 4); i = i | (i >> 2); i = i | (i >> 1); i = ~i; //加上bitCount } ``` #### copyLSB 將最小位移到符號位,在利用 sign extension 將所有位補成符號位的數 ```c int copyLSB(int x) { return x << 30 << 1 >> 30 >> 1; } ``` #### distinctNegation 考慮0和0x80000000就可以了。 ``` int distinctNegation(int x) { int k = x << 1; return !!(k); } ``` #### dividePower2 這題要 round to zero 負數要加, CSAPP 只有在浮點數稍微提一下,公式是喂狗來的, `bias=(1<<n)-1`,是目前看到唯一可以移除掉分之的作法。想破頭居然才兩分。 ```c int dividePower2(int x, int n) { int y = x >> 30 >> 1; return (x + (((1 << n) + (~1 + 1)) & y)) >> n; } ``` ##### evenBits ```c int evenBits(void) { int i=5; i=i|(i<<16); i=i|(i<<8); i=i|(i<<4); return i; } ``` #### ezThreeFourths 注意正負的判斷是判斷做完乘法後,已經溢位的數,如果溢位成負數就用負數做。 ```c int ezThreeFourths(int x) { int i=x+(x<<1); int y=i>>30>>1; i=(i+(3&y))>>2; return i; } ``` #### fitsBits 利用 sign extension 。 ```c int fitsBits(int x, int n) { int i = x << (32 - n); i = i >> (32 - n); i = i ^ x; return !i; } ``` #### fitsShort 減號可以刪掉,不過為了對應上一題,就寫這樣了。 ```c int fitsShort(int x) { int n = 16; int i = x << (32 - n); i = i >> (32 - n); i = i ^ x; return !i; } ``` #### floatAbsVal 注意NaN的格式 `x11111111XXXXXXXXXXXXXXXXXXXXXXX`,但 X 不能全為0。 ```c unsigned floatAbsVal(unsigned uf) { int temp = uf << 1; temp = temp >> 24; temp = ~temp; int cond = uf << 9; if ((!temp) && (cond)) return uf; unsigned i = 1 << 30 << 1; i = ~i; i = uf & i; return i; } ``` 要進入浮點數的題目了........ Total points: 61/228 估計接近一百題