實(shí)驗(yàn)1—DataLab
實(shí)驗(yàn)材料
- 一個(gè)能夠運(yùn)行的Linux系統(tǒng)或Unix
- 下載好make
- 能夠運(yùn)行32位程序的gcc
- 官網(wǎng)的實(shí)習(xí)手冊(cè)
實(shí)驗(yàn)要求
整數(shù)編碼規(guī)則:
將每個(gè)函數(shù)中的“return”語(yǔ)句替換為一個(gè)或?qū)崿F(xiàn)該函數(shù)的多行C代碼。你的代碼必須符合以下風(fēng)格:
int Funct(arg1, arg2,…){
/*簡(jiǎn)單描述你的實(shí)現(xiàn)如何工作*/
int var1 = Expr1;
…
int varM = ExprM;
varJ = ExprJ;
…
varN = ExprN;
return ExprR;
}
每個(gè)"Expr"都是只使用以下語(yǔ)句的表達(dá)式:
整數(shù)常量0到255 (0xFF),包括。你是不允許使用像0xffffffff這樣的大常量。
函數(shù)參數(shù)和局部變量(沒(méi)有全局變量)。
3.一元整型運(yùn)算 !~
- 二進(jìn)制整數(shù)運(yùn)算& ^ | + << >>
有些問(wèn)題進(jìn)一步限制了允許的運(yùn)算符的集合。每個(gè)“Expr”可以包含多個(gè)運(yùn)算符。你不不會(huì)被限制每行一個(gè)操作符。您被明確禁止:
使用任何控件結(jié)構(gòu),如if、do、while、for、switch等。
定義或使用任何宏。
在此文件中定義任何附加函數(shù)。
調(diào)用任何函數(shù)。
使用任何其他操作,如 &&,||,-,或 ?:。
使用任何形式的對(duì)象轉(zhuǎn)換。
使用除int以外的任何數(shù)據(jù)類(lèi)型,這意味著你不能使用數(shù)組、結(jié)構(gòu)體或聯(lián)合。
你可以假設(shè)你的機(jī)器:
使用二進(jìn)制補(bǔ)碼,int類(lèi)型為32位表示。
使用算術(shù)右移。
3.在轉(zhuǎn)換時(shí)可能會(huì)發(fā)生溢出
浮點(diǎn)編碼規(guī)則對(duì)于需要實(shí)現(xiàn)浮點(diǎn)運(yùn)算的問(wèn)題,編碼規(guī)則不那么嚴(yán)格。你可以使用循環(huán)和有條件的控制。你可以同時(shí)使用整數(shù)和無(wú)符號(hào)。您可以使用任意整數(shù)和無(wú)符號(hào)常量。你可以使用任何算法,對(duì)int或unsigned數(shù)據(jù)進(jìn)行邏輯或比較操作。
但您被明確禁止:
定義或使用任何宏。
在此文件中定義任何附加函數(shù)。
調(diào)用任何函數(shù)。
使用任何形式的對(duì)象轉(zhuǎn)換。
使用除int或unsigned以外的任何數(shù)據(jù)類(lèi)型。這意味著你不能使用數(shù)組、結(jié)構(gòu)體或聯(lián)合。
使用任何浮點(diǎn)數(shù)據(jù)類(lèi)型、操作或常量。
實(shí)驗(yàn)規(guī)則
你需要通過(guò)修改bits.c中的函數(shù)來(lái)完成實(shí)驗(yàn)。每次修改后,你都需要鍵入下面兩條指令來(lái)檢查正確性,這意味著你需要安裝好make。
make clean
make btest
然后,你可以鍵入下面這條指令來(lái)輸出結(jié)果
./btest
2.62 DataLab

題目一:只使用 ~ 和 & 實(shí)現(xiàn) ^
題目代碼與解答:
/*
* 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);
}
解答:
根據(jù)布爾代數(shù),代碼~(x & y)實(shí)現(xiàn)的是只要不同時(shí)為1則為1,只要我們除掉同時(shí)為0取1的情況便實(shí)現(xiàn)了異或,而代碼~x & ~y實(shí)現(xiàn)的是只有同時(shí)為0則為1,則~(~x & ~y)實(shí)現(xiàn)的是同時(shí)為0則為0,這樣就排除掉了同時(shí)為0取1的情況。
題目二:返回最小的補(bǔ)碼
題目代碼與解答:
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 1 << 31;
}
解答:
0x80000000即為最小的補(bǔ)碼。
題目三:判斷是否為補(bǔ)碼的最大值
題目代碼與解答:
/*
* 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) {
return !!(x+1) & !(~(x+x+1));
}
解答:
補(bǔ)碼的最大值為0x7fffffff,如果x是該值的話,那么x + x + 1應(yīng)該等于0xffffffff,按位取反則為0,邏輯運(yùn)算中,0為假,非0為真,邏輯非取邏輯反,則我們應(yīng)該返回 !(~(x+x+1)),它在x = 0x7fffffffff時(shí)返回1,而在其它時(shí)候返回0,不過(guò)請(qǐng)注意,當(dāng)x = 0xffffffff時(shí),它仍然會(huì)返回1,所以我們需要特判這個(gè)數(shù)。
題目四:判斷補(bǔ)碼所有的奇數(shù)位是否都為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 a = (0xAA << 8 ) + 0xAA;
int b = (a << 16) + a;
return ! ((x & b) ^ b);
}
解答:
奇數(shù)位全為1的掩碼為0xAAAAAAAA,我們需要利用移位技巧構(gòu)造這個(gè)掩碼,然后我們將x的偶數(shù)位置為0,最后再判斷它是否等于0xAAAAAAAA即可,判斷x == y可以采用!(x ^ y)來(lái)實(shí)現(xiàn),它利用了異或的性質(zhì)。
題目五:不用負(fù)號(hào)實(shí)現(xiàn) -x
題目代碼與解答:
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1;
}
解答:
這個(gè)題目很簡(jiǎn)單,我們返回它的按位反再加一即可。因?yàn)榘次蝗》吹臄?shù)與原來(lái)數(shù)相加為0xffffffff,再加1即等于0。
題目六:判斷x是否為ASCII碼
題目代碼與解答:
/*
* 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 a = x + (~0x30 + 1);
int b = 0x39 + (~x + 1);
int c = 0x1 << 31;
return !(a & c) & !(b & c);
}
解答:
這個(gè)題目要求我們判斷x是否大于等于0x30且小于等于0x39,我們可以推出這樣兩個(gè)不等式,① x + (- 0x30) >= 0;2②0x39 + (- x) >= 0。我們只需要比較最終結(jié)果是否大于等于0就可以了,這等同于符號(hào)位是否為0,這可以通過(guò) & 0x80000000得出,如果符號(hào)位為0,& 0x80000000的結(jié)果一定是0,取邏輯反則為1;反之,如果符號(hào)位為1,取邏輯反的結(jié)果應(yīng)該為0。
我們要思考一下加法溢出是否會(huì)對(duì)結(jié)果造成影響,如果第一個(gè)等式發(fā)生溢出,則有 x - 0x30 < -232,那么x < -232+ 0x30,如果x = Tmin,那么-x = Tmin,則 0x39 + (-x) = 0x39 + Tmin < 0,這不滿足不等式②;如果x != Tmin,那么有 -x > 232 - 0x30,那么有0x39 + (-x) = 0x39 + 232 - 0x30 = 232 + 0x9,這發(fā)生了正溢出,因此它的結(jié)果會(huì)小于0,同樣不滿足等式②,因此第一個(gè)等式溢出并不會(huì)影響答案。第二個(gè)等式溢出可同理分析。
題目七:實(shí)現(xiàn)表達(dá)式 x ? y : z
題目代碼與解答:
/*
* 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) {
int a = !!x;
int b = (a << 31) >> 31;
return (y & b) | (z & ~b);
}
解答:
分析代碼,如果x真則a = 1,b = 0xffffffff,y & b則置y為原數(shù),z & ~b則值z(mì)為0,那么 (y & b) | (z & ~b)返回的便是y;如果x假則a = 0,b = 0,y & b則置y為0,z & ~b則值z(mì)為原數(shù),那么 (y & b) | (z & ~b)返回的便是z
題目八:x <= y
題目代碼與解答:
* 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 s = y + (~x + 1);
int m = 1 << 31;
int a = !!(x & m);
int b = !!(y & m);
int c = !!(a ^ b);
int f = s & m;
return (c & !b) | (!c & !f);
}
解答:
這道題可以采用y + (- x) >= 0解答,但我們必須要保證它沒(méi)有溢出。如果x與y異號(hào),此時(shí)如果y為非負(fù)則返回1,否則返回0;如果x與y同號(hào),我們判斷y + (-x)是否大于0即可。代碼中a,b,f計(jì)算x,y,s的符號(hào),而c判斷x與y是否異號(hào)。
題目九:不使用 ! 實(shí)現(xiàn) !x
題目代碼與解答:
/*
* 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;
}
解答:
對(duì)于x而言,除了0和Tmin之外,x 與~x + 1互為相反(符號(hào)位相反)數(shù),這意味著肯定有一個(gè)數(shù)為負(fù),即符號(hào)位為1,將一個(gè)符號(hào)位為1的數(shù)邏輯右移31位后會(huì)得到0xffffffff,加1則為0;對(duì)于Tmin而言,~x + 1 = Tmin,這說(shuō)明它們的符號(hào)位也為1,即答案也會(huì)是0;對(duì)于0而言,x 與~x + 1的符號(hào)位都為0,這意味著最終的結(jié)果會(huì)是1。
題目十:一個(gè)數(shù)用補(bǔ)碼表示最少需要幾位
題目代碼與解答:
/* 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 s = x >> 31;
x = x ^ s;
int b16 = !!(x >> 16) << 4;
x = x >> b16;
int b8 = !!(x >> 8) << 3;;
x = x >> b8;
int b4 = !!(x >> 4) << 2;
x = x >> b4;
int b2 = !!(x >> 2) << 1;
x = x >> b2;
int b1 = !!(x >> 1) << 0;
x = x >> b1;
return b16 + b8 + b4 + b2 + b1 + x + 1;
}
解答:
不斷縮小范圍即可,b16表示32位中,高16為是否有一,如果有b16 = 1 << 4 = 1,那么x就會(huì)右移16位,把返回縮小到高16位到高32位這個(gè)范圍,如果沒(méi)有,則把范圍縮小到0位到高16位這個(gè)范圍,如此反復(fù),請(qǐng)不要忘了加上符號(hào)位!
題目十一:求2乘以一個(gè)浮點(diǎn)數(shù)
題目代碼與解答:
/*
* 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 >> 23) & 0xff;
int sign = (uf & (1 << 31));
int inf = 0x7f800000 | sign;
if (exp == 255)
return uf;
if (exp == 0)
return (uf << 1) | sign;
exp++;
if (exp == 255)
return inf;
return (uf & 0x807fffff) | (exp << 23);
}
解答:
這個(gè)題目要求我們熟悉浮點(diǎn)數(shù)的表示,先取出uf的階碼域exp,如果這個(gè)數(shù)是一個(gè)特殊值,我們什么也不做,直接返回;如果這個(gè)數(shù)是非規(guī)格化數(shù),得益于非規(guī)格化數(shù)到規(guī)格化數(shù)的平滑轉(zhuǎn)變,我們直接將uf左移一位即可,由于移除了符號(hào)位,請(qǐng)不要忘記添加上來(lái);如果這個(gè)數(shù)是規(guī)格化的數(shù),我們便將階碼加1,如果此時(shí)階碼 = 255,我們返回?zé)o窮大,否則的話,我們更新階碼并返回。
題目十二:將float轉(zhuǎn)換成int
題目代碼與解答:
/*
* 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 (incleuding 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 >> 23) & 0xff;
int sign = uf & (1 << 31);
if (exp == 255)
return 0x80000000;
if (exp == 0)
return 0;
int k = exp - 127;
int f = uf & 0x00ffffff;
if (k < 0)
return 0;
if (k > 31)
return 0x80000000;
if (k < 23)
f >>= (23 - k);
else
f <<= (k - 23);
if (sign)
return ~f + 1;
else
return f;
}
解答:
先考慮特殊情況,當(dāng)exp == 255或exp == 0時(shí),此時(shí)表示特殊值和非規(guī)格化的值,我們按要求返回。否則計(jì)算出階碼值k和小數(shù)域f,請(qǐng)注意這里f是需要多加一個(gè)1的,如果k < 0的話,我們返回0;如果k > 31的話,此時(shí)一定發(fā)生了溢出。其他情況下,我們根據(jù)k的值進(jìn)行移位,可以設(shè) f = [0000,0000,01X1X2,.......X23],它的真實(shí)值以二進(jìn)制表示為1.X1X2,.......X23,如果k < 23的話,意味著并不是所有的小數(shù)都能移位至整數(shù)部分,則我們必須要舍去剩下的(23 - k)的小數(shù)值,所以選擇右移23-k位;如果k >= 23,說(shuō)明所有的小數(shù)都可以移動(dòng)到整數(shù)部分,如果k = 23的話,這正是[0000,0000,01X1X2,.......X23]的位表示,如果k > 23,我們?nèi)孕柘蛴乙苿?dòng)k-23位。最后,別忘了它的正負(fù)。
題目十三:計(jì)算2.0x
題目代碼與解答:
/*
* 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 e = x + 127;
if (e >= 255)
return 0x7f800000;
if (x < 0)
return 0;
return e << 23;
}
解答:
我們可以計(jì)算1.0 * 2x來(lái)達(dá)到這個(gè)目的,先排除掉幾個(gè)值之外,我們將x + Bias作為階碼域返回即可,此時(shí)規(guī)格化的值會(huì)默認(rèn)小數(shù)為1.0。