任務:按照要求補充13個函數,會限制你能使用的操作及數量
- bitXor(x,y) 只使用 ~ 和 & 實現 ^
- tmin() 返回最小補碼
- isTmax(x) 判斷是否是補碼最大值
- allOddBits(x) 判斷補碼所有奇數位是否都是1
- negate(x) 不使用負號 - 實現 -x
- isAsciiDigit(x) 判斷 x 是否是 ASCII 碼
- conditional(x, y, z) 類似於 C 語言中的 x?y:z
- isLessOrEqual(x,y) x<=y
- logicalNeg(x) 計算 !x (不用 ! 運算符)
- howManyBits(x) 計算表達 x 所需的最少位數
- floatScale2(uf) 計算 2.0*uf
- floatFloat2Int(uf) 計算 (int) f
- floatPower2(x) 計算 2.0的x次方
/*
* 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))) & (~((~x) & y))); // 直接推公式,^可以使用~、&和|表示,而|又可以用~和&表示
}
推導一下公式就可以直接寫出來了。
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
int one = 1;
return (one << 31); // 最小的有符號數,符號位為1,其余都是0
}
有符號數是用補碼來表示的,Tmin表示最小補碼數,對於1個字節大小的補碼,最小補碼數形式為1000 0000,C語言中int類型占4字節,即32位,所以對1左移31位來構造最小補碼。
/*
* 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 neg1;
neg1 = !(~x); // 如果x為-1, 則neg1為1,否則neg1為0,這里是為了排除-1的干擾
return !((~(x+1)^x)|neg1); // 給x加1,再翻轉,最后和自身取異或,如果x為Tmax,則返回1,否則返回0
}
函數功能是判斷x是否是有符號數的最大值,也就是補碼最大值,還是拿1個字節來看,最大補碼數的形式為0111 1111,代碼中的neg1是為了將-1單獨判斷出來,因為如果只使用return后面那句(!(~(x+1)^x))的話,會導致當x=-1的時候也會返回1,判斷出現錯誤,而改變后的返回結果可以排除-1的干擾。
/*
* 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 mask = (0xAA << 8) + 0xAA;
mask = (mask << 16) + mask; // 構造掩碼
return !((x & mask) ^ mask); // &操作將x的奇數位取出,偶數位置0,之后再與掩碼異或判斷是否滿足條件
}
構造掩碼操作即可,將掩碼和x進行與操作,可以讓x的奇數位置不變,而偶數位置變為0。
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1; // 補碼取相反數操作:按位取反再加一
}
直接套用公式。
/*
* 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 negative = 1 << 31;
int lessthan = ~(negative | 0x39); // 構造上界,如果超過,則符號位變為1
int greatthan = (~(0x30) + 1); // 構造下界,如果不足,則符號位變為1
lessthan = negative & (lessthan + x) >> 31;
greatthan = negative & (greatthan + x) >> 31;
return !(lessthan | greatthan); // 判斷符號位是否為1
return 2;
}
通過上下界來判斷輸入的x是否在0x30~0x39的范圍中,使用x分別加上界和下界,當x不在這個范圍中時,通過判斷符號位的變化來得出判斷。
/*
* 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) {
x = !!x; // 判斷x是否為0,若x=0,則x賦值為0;若x不為0,則x賦值為1
x = ~x + 1; // 得到x的補碼,0的補碼還是0,1的補碼為-1(二進制序列全1)
return (x&y)|(~x&z); // 若x為0,則返回z;若x為1,則返回y
}
重點在於return語句,這個操作可以根據x的不同來返回不同的值。
/*
* 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 minusx = ~x + 1; // 得到-x
int result = y + minusx; // 得到y - x
int sign = (result >> 31) & 1; // 判斷result的符號,如果y>=x,則sign等於0,否則等於1
int xsign = (x >> 31) & 1; // 取出x的符號
int ysign = (y >> 31) & 1; // 取出y的符號
int bitXor = xsign ^ ysign; // 判斷x和y符號是否一致
return ((!bitXor)&(!sign)) | ((bitXor&xsign)); // 要么x和y符號相同並且x<=y,要么x和y符號不同並且x<0
}
判斷方法:如果x和y同符號,當x<=y則返回1;或者如果x和y不同符號,那么當x<0則返回1;其余情況返回0。
這里根據y-x的結果的符號來判斷x和y的大小。
/*
* 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;
}
/* 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) {
// 原理:對於正數,從高位到低位,找第一個位是1的(比如是n),再加上符號位,則最少需要n+1個位;
// 對於負數,從高位到低位,找第一個位是0的(比如是n),則最少需要n位
int b16, b8, b4, b2, b1, b0; // 表示0~15、16~23、24~27、28~29、30、31的位置處是否含有1,如有,則對其賦值需要的位數
int sign = x >> 31; // 取符號位
x = (sign&~x)|(~sign&x); // 如果x為正則不變,x為負則取反,這里是為了統一正負數,我們之后只用找到含有1的位置即可
b16 = !!(x >> 16) << 4;// 先看高16位是否含有1,若有則表示至少需要16位,所以給b16賦值為16(1 << 4 = 16)
x = x >> b16; // 若有1,則原數右移16位,因為上面已經確定是否至少需要16位(針對0~15);若沒有1,則b16為0,x不用移位,繼續往下面判斷
b8 = !!(x >> 8) << 3; // 看剩余位的高8位是否含有1,若有則表示至少還需要8位,給b8賦值為8
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; // 最后加上符號位
}
注釋已經寫的很清楚了,可以邊看代碼邊打草稿,很容易理解。
/*
* 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) {
int exp = (uf&0x7F800000) >> 23; // 取出階碼
int sign = uf&(1 << 31); // 取符號位
if (exp == 0) return uf<<1|sign; // 若為非規格數,直接給uf乘以2后加上符號位即可
if (exp == 255) return uf; // 若為無窮大或者NaN,直接返回自身
exp = exp + 1; // 若uf乘以2(也就是階碼加1)后變成255,則返回無窮大
if (exp == 255) return (0x7F800000|sign);
return (exp << 23)|(uf&0x807FFFFF); // 返回階碼加1后的原符號數
}
需要了解計算機內浮點數的表示方法,了解浮點數中的規格數、非規格數、無窮大和未定義的區別和表示。
我們先看如何表示浮點數:
這里的uf類型為unsigned int,並不是浮點數,但是我們將uf看作為單精度類型,它有32位,最高位是符號位,之后8位保存指數信息,最后23位保存小數信息,所以在代碼中我們可以看到,我們通過和0x7F800000取與操作來獲得指數信息,再右移23位取出這一部分。
浮點數有幾種特殊情況:
1.若exp部分全為0(exp = 0),則是非規格化數,它是一種非常接近0的數;
2.若exp部分全為1(exp = 255),當小數部分全為0時,表示無窮大;當小數部分不為全0時,表示未初始化數據NaN;
3.以上兩種情況以外,就是規格化數。
所以我們需要判斷uf是哪一種浮點數,並根據它的類型來進行相應的操作。
/*
* 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) {
int exp = ((uf&0x7F800000) >> 23) - 127; // 計算出指數
int sign = uf >> 31; // 取符號位
int frac = ((uf&0x007FFFFF) | 0x00800000);
if (!(uf&0x7FFFFFFF)) return 0; // 若原浮點數為0,則返回0
if (exp > 31) return 0x80000000; // 若原浮點數指數大於31,返回溢出值
if (exp < 0) return 0; // 若浮點數小於0,則返回0;
if (exp > 23) frac = frac << (exp - 23); // 將小數轉化為整數
else frac = frac >> (23 - exp);
if (!((frac >> 31) ^ sign)) return frac; // 判斷是否溢出,若符號位沒有變化,則沒有溢出,返回正確的值
else if (frac >> 31) return 0x80000000; // 原數為正值,現在為負值,返回溢出值
else return ~frac + 1; // 原數為負值,現在為正值,返回相反數
}
需要了解整數和浮點數之間的轉化方法,我們要做的就是將浮點數中的指數部分和小數部分取出來,然后通過這兩部分來轉化為整數,具體操作可以看代碼,在這個過程中還要判斷是否會產生溢出,以及浮點數是否為規格數等情況,如果產生溢出,我們需要返回一個特定的溢出值。
這里有一個將整數轉化為浮點數的例子:
/*
* 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) {
int INF = 0xFF << 23; // 設定一個最大值,也就是階碼位置都為1
int exp = x + 127; // 計算階碼
if (exp <= 0) return 0; // 階碼小於等於0,則返回0
if (exp >= 255) return INF; // 階碼大於等於255,則返回INF
return exp << 23;
}
所有代碼及相關實驗說明材料都在這里