Data Lab
Link: csapp lab(該鏈接被限制訪問)
也可自行在github中搜索csapplab,以找到實驗原文件。
操作系統:linux
one
bitXor
- bitXor - x^y using only ~ and &
- Example: bitXor(4, 5) = 1
- Legal ops: ~ &
- Max ops: 14
- Rating: 1
bitXor:實現位級異或,限制操作: ~ &
int bitXor(int x, int y) {
return ~((~(x&~y)) & (~(~x&y)));
//XOR的與或非實現、OR的與非實現,兩者組合,達成xor的與非實現
}
思路:
XOR的與或非實現: (x & ~ y)|( ~ x & y); OR的與非實現: ~ (~ a & ~ b)
time
- tmin - return minimum two's complement integer
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 4
- Rating: 1
tmin:位級實現輸出Tmin
一開始沒認真看INTEGER CODING RULES的要求,后來才發現,僅允許使用0 ~ 255之間的數值,-1 << 31 雖然是對的,但不符合要求
int tmin(void) {
return 1 << 31; //0x80000000
}
two
isTmax
- isTmax - returns 1 if x is the maximum, two's complement number,
- and 0 otherwise
- Legal ops: ! ~ & ^ | +
- Max ops: 10
- Rating: 1
isTmax:判斷輸入的數值是否為Tmax即0x7FFFFFFF,是,輸出1,否則,輸出0
不知道為啥上面題目是tmin,這里就是Tmax,為啥大小寫不統一呢?絕對是出題老師偷懶了。
int isTmax(int x) {
return !((x^~(x+1))|(!(~x))); //或者!(x^~(x+1)) & !(!(x+1));
}
思路:
由於Tmax == ~(Tmax+1),|左邊利用異或^充當判斷==,相等其值為0, |右邊排除-1即0xffffffff (因為-1 == ~(-1+1));tmax按位取反再按數值取反后,為0, -1按位取反再按數值取反后,為1
allOddBits
- 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: ! ~ & ^ | + << >>T
- Max ops: 12
- Rating: 2
allOddBits:判斷一個數的奇數位(odd)是否全為1,是,輸出1,否則,輸出0
int allOddBits(int x) {
int mask = 0xaa | 0xaa << 8;
mask = mask | mask << 16;
x = x & mask;
return !(x^mask);
}
思路:
先構造0xAAAAAAAA,利用 <<、|即可,再用x XOR x的必為0的性質,邏輯取反即可
negate
- negate - return -x
- Example: negate(1) = -1.
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 5
- Rating: 2
negate:取相反數
不知道的時候是真的不知道= =
int negate(int x) {
return (~x)+1; //按位取反,再加1即可
}
three
isAsciiDigit
- 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
isAsciiDigit:判斷一個數是否在0x30 <= x <= 0x39之間,是,輸出1,否則,輸出0
自己的思路把前半部的信息給清除了,不能用。還是大佬強,還具有擴展性,改變上下限數值就是改變范圍了。
int isAsciiDigit(int x) {
int sign = 1<<31;
return !(((sign&((~(sign|0x39))+x))>>31) | ((sign&(~0x30+1+x))>>31));
}
思路:
上限(左邊),目的是使輸入的數大於0x39時,真值為1;下限(右邊),目的是使輸入的數小於0x30時,真值為1,
當兩邊的真值為0時,才居於范圍之間,輸出1
先取一個符號位sign即0x80000000
左邊:
0x39按位或sign后再取反,目的是得到一個低8位為0xc6,符號位為0,其它位為1的位級。
該位級+x后,若x大於0x39則其符號位變為1,反之為0
&sign取符號位后,再右移31。若其值大於0x39則為-1,反之為0。
右邊:
0x30按位取反+1,目的是得到一個低8位為0xd0,符號位為1,其它位為1的位級。
該位級加x后,若x小於0x30則其符號位仍為1,反之為0
&sign取符號位后,再右移31。若其值小於0x30為-1,反之為0。
conditional
- conditional - same as x ? y : z
- Example: conditional(2,4,5) = 4
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 16
- Rating: 3
conditional:用位級運算實現三目運算符(x ? y : z)
思路往往可以更簡潔
//法一
int conditional(int x, int y, int z) {
x = (!!x)<<31>>31;//use: overturn( logic); by <<31>>31
return (x&y)|(~x&z);
}
思路:
用x構造出全1或全0,再使全1、全0分別與x、y對應。
x邏輯取反兩次得真值,再<<31>>31,使真值1變為全1即-1,真值0不變
此時,若x為-1,則&y得到y,且按位取反x,並&z清空;若x為0,則&y清空,且按位取反x,並&z得到z
而后按位或輸出
//法二
int conditional(int x, int y, int z) {
x = !!x;
x = ~x+1;//use: overturn(bits, logic); by -1+1 = 0;0xfffffffe+1 = 0xffffffff = -1
return (x&y)|(~x&z);
}
思路:
同法一類似,只是構造全1或者全0的方法不同
用x構造出全1或全0,再使全1、全0分別與x、y對應。
x邏輯取反兩次得真值,再按位取反后+1,使真值1變為全1即-1,真值0不變
此時,若x為-1,則&y得到y,且按位取反x,並&z清空;若x為0,則&y清空,且按位取反x,並&z得到z
而后按位或輸出
//法三
int conditional(int x, int y, int z) {
int neg_1 = ~0
return ((!x)+neg_1)&y | ((!!x)+neg_1)&z;
}
思路:
x不為0時,輸出y;x為0時,輸出z。想辦法使兩者分別對應,利用非!翻轉(x不為0翻轉1次得全0 or x為0翻轉2次得全0)使兩者分別對應
再用-1(neg_1)配出我們要的全1,以便&x或y()。
運算過程:
左邊:若x不為0,則翻轉一次后-1,為全1,可得y值;若x為0,則翻轉再-1,為全0,可清空y值;
右邊:若x不為0,則翻轉兩次后-1,為全0,可清空z值;若x為0,則兩次翻轉再-1,為全1,可得到z值;
兩邊取或,可輸出數字(return竟然可以輸出數值!!哭笑)
isLessOrEqual
- isLessOrEqual - if x <= y then return 1, else return 0
- Example: isLessOrEqual(4,5) = 1
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 24
- Rating: 3
isLessOrEqual:判斷是否x <= y,是,輸出1,否則,輸出0
一般自己寫的都有點長
int isLessOrEqual(int x, int y) {
return !!(!(x^y) ^ ((x>>31)^(y>>31)&(x>>31)) ^ !((x>>31)^(y>>31))&(x+((~y)+1))>>31); // if not use !!() will return -1
}
思路:
判斷x<=y,分成三段解決,任意情況成立即可,則用XOR連接,1.兩者相等;兩者不相等時,2.sign不同;3.sign相同。
1.XOR清零再邏輯非即可
2.先限定於sign不同的情況,利用XOR
再&(x>>31),保留x的sign位,若sign=1,x為負數,則全1輸出;若sign=0,x為正數,則全0輸出
3.先限定於sign相同的情況(即排除sign不同的情況),再利用作差,x-y,小於0時,符合題意,且sign位為1,最后>>31,分割出全0和全1
XOR連接完后,為滿足符合return 1, 否則return 0,要取兩次邏輯非
logicalNeg
- 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
logicalNeg:實現邏輯非,限制操作:~ & ^ | + << >>
//法一
int logicalNeg(int x) {
int sign = 1<<31;
int all1 = ~0;
return ((((~x)|sign)+1)>>31)+1 & (x^sign)>>31 ; //((x^sign)>>31)
}
思路:
實現邏輯非!,即實現0輸出1,非0輸出0。
首先要分割0和非0部分,辦法是0將保持為0,非0全部轉換為負數使其符號位為1。,
用 ~ x 翻轉,~ 0 = -1,非0保持不變 ;
(~ x) | sign),使-1不變,非0數全部轉換為負數且最大值為-2,但注意到x=sign經運算后也為-1,用&(x^sign)>>31剔除,同時,使x=0時,第0位為1;
((~ x) | sign)+1),使-1變0,負數最大值此時為-1;
((~ x) | sign)+1)>>31,0不變,負數全為-1;
((((~ x) | sign)+1)>>31)+1,0變1,-1全變為0;
((((~ x) | sign)+1)>>31)+1 & (x^sign)>>31,x=0時,(左邊1 & 右邊-1)輸出1;x為非0數時,(左邊0 &右邊任何數)輸出0;
法二:
int logicalNeg(int x) {
return ((x|(~x+1))>>31)+1;
}
思路:
利用補碼(取反+1)的性質,0和Tmin的補碼為本身,其它數值的補碼為其相反數;
0與其補碼按位或之后,其值為全0,Tmin與其補碼、其它數值與其補碼,按位或之后,符號位為1;
然后>>31,0不變,Tmin、其它數值為全1即-1;
而后+1,0變為1,Tmin、其它數值為0;
howManyBits
- 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
howManyBits:求一個數最少要用多少位表示
這道題想了很久,因為允許90個ops,就慢慢找規律,最終想出最高位和次最高位相異時,該數的位數可以確定,但實際只能從1位到32位一個個判斷,總共操作接近300ops,只能翻起答案來= =,知道會用重復判斷的方法,沒想到是二分法這么巧妙
int howManyBits(int x) {
int b16,b8,b4,b2,b1,b0;
int sign = x>>31;
x = (sign&~x)|(~sign&x);//x為正數不變;x為負數按位取反,其符號位變為0,可將負數當作正數來求其所需最少的表達位數
// 二分法,不斷縮小范圍
b16 = !!(x>>16)<<4;//高十六位是否有1
x = x>>b16;//如果有(至少需要16位),則將原數右移16位
b8 = !!(x>>8)<<3;//剩余位高8位是否有1
x = x>>b8;//如果有(至少需要16+8=24位),則右移8位
b4 = !!(x>>4)<<2;//剩余位高4位是否有1
x = x>>b4;//如果有(至少需要16+8+4=28位),則右移4位
b2 = !!(x>>2)<<1;//剩余位高2位是否有1
x = x>>b2;//如果有(至少需要16+8+4+2=30位),則右移2位
b1 = !!(x>>1);//剩余位高1位是否有1
x = x>>b1;// 如果有(至少需要16+8+4+2+1=31位),則右移位
b0 = x;//b0為x,數值為1或者0
return b16+b8+b4+b2+b1+b0+1;//+1表示加上符號位
}
思路:
如果是一個正數,則需要找到它最高為1的是第幾位(假設該位是第n位),再加上符號位0(計數為1),那么它最少需要n+1位來表示;
如果是一個負數,則需要找到它最高為0的是第幾位(假設該位是第m位),那么它最少需要m位來表示
float
一開始是真的懵,想來是浮點數位級表示學得比較模糊
floatScale2
- 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
floatScale2:用unsigned的位級來表示一個浮點數uf,同時,對浮點數uf乘2
unsigned floatScale2(unsigned uf) {
int exp = (uf&0x7f800000)>>23;//取指數exp
int sign = uf&0x80000000;//取符號位sign
if(exp==0) return uf<<1|sign;//輸出(非規化數)*2或者0*2
if(exp==255) return uf;//輸出NaN或者INF(無窮,包括正無窮和負無窮)
exp++; //計算(規化數)*2,
if(exp==255) return 0x7f800000|sign;//(規化數)*2后,若指數exp全為1即INF,則輸出0x7f800000|sign
return (exp<<23)|(uf&0x807fffff);//(規化數)*2后,若指數exp不全為1就仍為規划數,則輸出(exp<<23)|(uf&0x807fffff)
}
思路:
區分規化數、非規化數、NaN、INF,注意區分的時候,都要乘2
第5行,不用exp<<1的原因是:雖然能達到*2的目的,但可能會使exp越出255,突破exp限定的8位
floatFloat2Int
- 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
floatFloat2Int:用unsigned位級表示浮點數uf強制轉換為int整型數
int floatFloat2Int(unsigned uf)
{
int sign = (uf >> 31) & 1;//取符號位sign
int bias = 127;//偏置值為(2 ^ (8-1)) - 1
int exp = (uf >> 23) & 0xFF;//取指數exp
int E = exp - bias; //指數exp的真實值,即階碼E
int frac = uf & 0x007FFFFF;//取小數字段frac,注意符號位被剔除了
int M = frac | 0x00800000; //將第23位置1,即階碼的最后1位置1,因為:規化數之中尾數M的范圍是(1 ~ 2-ξ)
int tar;
if (sign) sign = -1;
else sign = 1;//符合位取(-1) ^ 0=1 或者 (-1) ^ 1=-1 ,用以保持正數,或者轉為負數
if (E >= 31)
return 0x80000000;
if (E < 0)
return 0;
if (E >= 23)
tar = sign * (M << (E - 23));//E大於23時,則M左移(E - 23),同時考慮正負數
else if (E < 23)
tar = sign * (M >> (23 - E));//E小於23時,則M右移(23 - E),同時考慮正負數
return tar;
}
思路:
區分float型的規化數、非規化數、INF、NaN,
INF、NaN:其E >= 31 計算階碼值2^E,超出int型Tmax == 0x7fffffff,直接輸出0x80000000
非規化數:其E < 0 計算階碼值2^E,為小數,直接輸出0
規化數: 其31> E >=0 計算階碼值2^E,居於1 ~ 2^30之間,需要左右移
左右移原理見 CSAPP P82
其結論是:階碼E大於尾數位數(float型取frac字段位數,共23位)時,則M左移(23 - E)(最多移8位);階碼E小於尾數位數,則M右移(23 - E)(最多移23位)
原解法對於E=23時,直接輸出tar=0;對於E=31時,將其看作仍可以做M左移運算,事實上2 ^ E = 2 ^ 31,已經超過int型Tmax = (2 ^ 31) - 1
但檢測可以通過 糾正1:雖然超過了Tmax,但沒有超過Tmin = - 2 ^ 31,故,對於E=31時,將其看作仍可以做M左移運算。糾正2:只要E取31時,左移時,必然移動8位到符號位且置1,因為:規化數的階碼E最后1位為1。那么,符號位為0,frac字段全0時,其值為2 ^ 31,左移8位后為0x80000000;符號位為0,frac字段非0時,其值超過2 ^ 31,要置為INF(0x80000000);符號位為1,frac字段全0時,其值為-2 ^ 31,左移8位后為0x80000000;符號位為1,frac字段非0時,其值為超過-2 ^ 31,要置為INF。綜合下來,E = 31時,可以直接置為INF。
筆者做出修正
對於E=23時,輸出tar = sign*M;對於E=31時,直接輸出0x80000000(本題Anything out of range return 0x80000000)
floatPower2
- 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
floatPower2:輸入一個int x,用unsigned位級表示2.0 ^ x
unsigned floatPower2(int x) {
unsigned INF = 0xff << 23;
int exp = x + 127;
if(exp >= 255) return INF;
if(exp < -23) return 0;
if(exp <= 0) return 0x00400000>>(~exp+1);
return exp << 23;
}
思路:
首先需要定義INF,為exp全1即0xff << 23;其次定義小於2 ^(-126-23)為0
x的取值即為E,exp = E + bias,可計算出指數exp,而后
考慮臨界值:
bin(x) = 0 00000000 00000000000000000000001,此數是2 ^ (-126-23),(最小值)
即exp = 0時情況;exp小於等於0,我們取 0x00400000>>( ~exp+1)
bin(x) = 0 00000001 00000000000000000000000,此數是2 ^ (-126),
即exp = 1時情況;exp居於0 ~ 254時,我們取exp << 23
bin(x) = 0 11111111 00000000000000000000000 ,此數是2 ^ (128),(大於最大值)
即exp = 255時情況。exp大於等於exp全1,我們取INF
以及,exp小於-23時,超出最小值,我們取0。
附圖:

感想:
- 雄關漫道真如鐵,而今邁步從頭越
- 從9.4號開始做這個lab,一直到9.12號,一共9天。難度真是比較大,終於感受到很名校同學的差距了(特別是那道ASCII)。平時很少做這樣的題。他們是把CSAPP當作ICS(計算機導論)來上的,我得向這些他們靠近。轉專業並降級的我已經落后了不少,是時候好好學了。
- 現在看難度1、2覺得很容易,但一開始做的時候還是一頭霧水的。位級運算的不但功能十分強大,而且還極大地減少運算時間。之后還得花點心思鞏固。英文閱讀能力非常需要提高,浮點題那一塊,題目都看不太懂。以后做題前先翻翻書,想想知識點,不要直接憑空硬莽。
- 如有謬誤,敬請指正。
- Memory Dot,我的個人博客,歡迎來玩。
