# 2018q3 Homework5 (bits)
contributed by < [`jason53415`](https://github.com/jason53415) >
## 測試環境
```shell
$ uname -a
Linux scream-47860 4.15.0-38-generic #41-Ubuntu SMP Wed Oct 10 10:59:38 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
```
## 函式原理
### allEvenBits
目標:回傳是否所有偶數位皆為 1
```C=
int allEvenBits(int x)
{
x = x & (x >> 16);
x = x & (x >> 8);
x = x & (x >> 4);
x = x & (x >> 2);
return x & 1;
}
```
類似於將整個 32 bits 不斷對折的概念,其結果等同於將所有的偶數位取出並兩兩做 AND 運算直到留下最後一個位元,因此只要其中一個偶數位是 0,則終將導致對後留在最低位的數字為 0。
### bitMatch
目標:只利用 `~` 與 `&` 實做,使函式回傳 `x` 與 `y` 中哪些位元是相同的
```C=
int bitMatch(int x, int y)
{
return ~(~(x & y) & ~(~x & ~y));
}
```
直觀的想法是做 XOR 運算後再取 NOT,就可以得到所有 `x` 與 `y` 中相同的位元,亦即 `~(x ^ y)`,不過因為題目限制的關係,所以要先把中間的 XOR 部份轉換成等價的 `~(x & y) & (x | y)`,最後因為 OR 同樣不能使用,所以再把 `(x | y)` 利用 De Morgan's laws 轉換成 `~(~x & ~y)` 就是最後的結果了。
### bitReverse
目標:將 `x` 中的所有位元按順序顛倒過來
```C=
int bitReverse(int x)
{
int mask1 = 0xff | (0xff << 8); // 0x0000ffff
int mask2 = 0xff | (0xff << 16); // 0x00ff00ff
int mask3 = 0x0f | (0x0f << 8);
mask3 = mask3 | (mask3 << 16); // 0x0f0f0f0f
int mask4 = 0x33 | (0x33 << 8);
mask4 = mask4 | (mask4 << 16); // 0x33333333
int mask5 = 0x55 | (0x55 << 8);
mask5 = mask5 | (mask5 << 16); // 0x55555555
x = ((x >> 16) & mask1) | ((x & mask1) << 16);
x = ((x >> 8) & mask2) | ((x & mask2) << 8);
x = ((x >> 4) & mask3) | ((x & mask3) << 4);
x = ((x >> 2) & mask4) | ((x & mask4) << 2);
x = ((x >> 1) & mask5) | ((x & mask5) << 1);
return x;
}
```
首先我們生成 5 個 mask,分別各是由低位到高位以 16、8、4、2、1 個連續的 1 與同樣數量連續的 0 交錯出現,亦即 `0x0000ffff`、`0x00ff00ff`、`0x0f0f0f0f`、`0x33333333`、`0x55555555`,每一個 mask 可以用來交換相鄰的連續 16、8、4、2、1 個位元的內容,因此只要而將所有位元利用這 5 個 mask 交換完後,就可以得到完全顛倒的結果了。
### fitsBits
目標:如果 `x` 可以用 `n` 個位元的 2 補數表示的話回傳 1
```C=
int fitsBits(int x, int n)
{
int shift = 33 + ~n;
return !((x << shift >> shift) ^ x);
}
```
如果一個數字可以用 `n` 個位元來表示的話,代表高位的 `32 - n` 個位元都跟 sing bit 相同,因此只要將數字左移 `32 - n` 再右移 `32 - n` 個位元後仍和原數字一樣,就代表這個數字是可以用 `n` 個位元來表示的。實做時因為題目限制不能使用減號,因此改使用 `32 + (1 + ~n)`,也就是 `33 + ~n` 來代替,並且以 shift 過後的值與原數字做 XOR 後是否等於 0 來判斷是否相等。
### floatIsEqual
目標:判斷兩個浮點數是否相等
```C=
int floatIsEqual(unsigned uf, unsigned ug)
{
int same = !(uf ^ ug);
int notnan = !!((~(uf >> 23) & 0xff) | !(uf << 9));
int iszero = !((uf | ug) ^ (0x1u << 31));
return (same & notnan) | ((!same) & iszero);
}
```
要判斷兩個浮點數是否相等可以從兩個條件下手:
1. 除了 NaN 之外,若兩個浮點數每一個位元都相同,則兩個浮點數必相等。
2. 除了 +0 與 -0 之外,若兩個浮點數有任何一個位元不相同,則兩個浮點數必不相等。
因此以上的實做使用了三個變數,分別是 `same` 代表了兩個浮點數是否每一個位元都相同,`notnan` 代表了其中一個數不是 NaN,`iszero` 代表兩個數不是 +0 就是 -0,接著就可以用上面列出的條件來判斷是否相等。
### greatestBitPos
目標:回傳最高位的 1 的所在位數
```C=
int greatestBitPos(int x)
{
int shift = !!(x >> 16) << 4;
int bit = shift;
x = x >> shift;
shift = !!(x >> 8) << 3;
bit = bit + shift;
x = x >> shift;
shift = !!(x >> 4) << 2;
bit = bit + shift;
x = x >> shift;
shift = !!(x >> 2) << 1;
bit = bit + shift;
x = x >> shift;
shift = !!(x >> 1);
bit = bit + shift;
x = x >> shift;
return x << bit;
}
```
使用類似於 binary search 遞迴搜尋的概念,首先對於一個 $2n$ 個位元的數字,確認了高位的 $n$ 個位元中是否有 1 後,就可以專注於高位的 $n$ 個位元或低位的 $n$ 個位元,利用相同的方法繼續尋找最高位的 1。實做時則利用了連續兩次的 `!` (logical NOT) 運算,來取得一個非 1 即 0 的數字,代表是否有任何 1 的存在,再將這個得到的 1 或 0 左移 $\log_2n$ 位後就可以得到 $n$ 或 0,代表最高位至少在多少位以上,記下這個目前得知的最高位數,並將原始數字右移剛剛得到 $n$ 或 0 位元後,我們就可以用同樣的方法繼續搜尋低位的 $n$ 位元,而陸續加總每一輪的結果就可以得到所要求的最高位數。
### isAsciiDigit
目標:判斷 `x` 是否是 ASCII 中的數字
```C=
int isAsciiDigit(int x)
{
int condition1 = !((x >> 4) ^ 0x3);
int condition2 = !((x & 0x8) ^ 0x0);
int condition3 = !((x & 0x6) ^ 0x0);
return condition1 & (condition2 | condition3);
}
```
`x` 若要是 ASCII 中的數字,則必須符合 `0x30 <= x <= 0x39`,實做中則將前述的條件拆成三個可以快速用 bit operation 判斷的條件 :
1. `x` 是否是一個第五位及第六位皆是 1,其餘所有更高位元皆為 0 的數字,即二進制表示符合 0011XXXX$_2$ (X 代表可 0 可 1)
2. `x` 是否是一個第四位是 0 的數字,即二進制表示符合 XXXX0XXX$_2$
3. `x` 是否是一個第二位及第三位皆是 0 的數字 (XXXXX00X$_2$)
最後我們只要判斷是否第一個條件成立,並且第二及第三個條件有其中一個符合即可。
## 分數
```shell
$ make check
gcc -O0 -g -Wall -Werror -m32 -lm -o btest bits.c btest.c decl.c tests.c
./btest
Score Rating Errors Function
4 4 0 absVal
3 3 0 addOK
2 2 0 allEvenBits
2 2 0 allOddBits
2 2 0 anyEvenBit
2 2 0 anyOddBit
4 4 0 bang
1 1 0 bitAnd
4 4 0 bitCount
3 3 0 bitMask
1 1 0 bitMatch
1 1 0 bitNor
1 1 0 bitOr
4 4 0 bitParity
4 4 0 bitReverse
1 1 0 bitXor
2 2 0 byteSwap
3 3 0 conditional
4 4 0 countLeadingZero
2 2 0 copyLSB
2 2 0 distinctNegation
2 2 0 dividePower2
1 1 0 evenBits
3 3 0 ezThreeFourths
2 2 0 fitsBits
1 1 0 fitsShort
2 2 0 floatAbsVal
4 4 0 floatFloat2Int
4 4 0 floatInt2Float
2 2 0 floatIsEqual
3 3 0 floatIsLess
2 2 0 floatNegate
4 4 0 floatPower2
4 4 0 floatScale1d2
4 4 0 floatScale2
4 4 0 floatScale64
4 4 0 floatUnsigned2Float
2 2 0 getByte
4 4 0 greatestBitPos
4 4 0 howManyBits
2 2 0 implication
4 4 0 intLog2
3 3 0 isAsciiDigit
2 2 0 isEqual
3 3 0 isGreater
3 3 0 isLess
3 3 0 isLessOrEqual
2 2 0 isNegative
2 2 0 isNonNegative
4 4 0 isNonZero
2 2 0 isNotEqual
4 4 0 isPallindrome
2 2 0 isPositive
4 4 0 isPower2
1 1 0 isTmax
1 1 0 isTmin
1 1 0 isZero
2 2 0 leastBitPos
4 4 0 leftBitCount
4 4 0 logicalNeg
3 3 0 logicalShift
4 4 0 maximumOfTwo
4 4 0 minimumOfTwo
1 1 0 minusOne
3 3 0 multFiveEighths
2 2 0 negate
2 2 0 oddBits
3 3 0 remainderPower2
3 3 0 replaceByte
3 3 0 rotateLeft
3 3 0 rotateRight
4 4 0 satAdd
3 3 0 satMul2
3 3 0 satMul3
2 2 0 sign
4 4 0 signMag2TwosComp
1 1 0 specialBits
3 3 0 subtractionOK
1 1 0 thirdBits
1 1 0 tmax
1 1 0 tmin
4 4 0 trueFiveEighths
4 4 0 trueThreeFourths
4 4 0 twosComp2SignMag
1 1 0 upperBits
Total points: 228/228
```