CSAPP DATA LAB1————位運算
1題目與解答:
./*
* bitAnd - x&y using only ~ and |
* Example: bitAnd(6, 5) = 4
* Legal ops: ~ |
* Max ops: 8
* Rating: 1
*/
int bitAnd(int x, int y) {
return ~(~x|~y);
}
題意解析:
這就是實現與的功能,由邏輯概念A&B=!(!A|!B) 很容易解決
2.題目與解答
/*
* getByte - Extract byte n from word x
* Bytes numbered from 0 (LSB) to 3 (MSB)
* Examples: getByte(0x12345678,1) = 0x56
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 6
* Rating: 2 (x>>8*n)&0xff
*/
int getByte(int x, int n) {
return ((x>>(n<<3))&0xff);
}
題意解析:這題的意思就是取位值
對於0x12345678
當n=0時,return 0x78
當n=1時,return 0x56
當n=2時,return 0x34
當n=3時,return 0x12
解答過程與思考:
首先,在這個過程中,x應先右移8*n位再取最低的8位數值
即 return (x>>8*n)&0xff
但是 不能用* 所以採用移位
return ((x>>(n<<3))&0xff);
3.題目與解答
/*
* logicalShift - shift x to the right by n, using a logical shift
* Can assume that 0 <= n <= 31
* Examples: logicalShift(0x87654321,4) = 0x08765432
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int logicalShift(int x, int n) {
int mask=~(((1<<31)>>n)<<1);
return (x>>n)&mask;
}
題意解析:
這題的意思就是邏輯右移位(預設為算術右移位)
解答過程與思考:
首先,考慮0x0000 0000 =>return x>>n
再考慮正數,0x0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
也只需要return x>>n
再考慮負數 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
取(0x-87654321,4)
1000 7 6 5 4 3 2 1 右移4位=》1111 1000 7 6 5 4 3 2
發現符號位對結果產生了干擾
所以 新增一個掩碼(掩碼只需要把符號位轉為0)
對於這一個負數的 掩碼應該為0x0FFFFFFF
若n=5 則掩碼為0x07FFFFFFF....
觀察這些掩碼,很容易發現,若右移n位,則前n位應為0
所以
嘗試將函式寫為
int logicalShift(int x, int n) {
int mask=~(((1<<31)>>(n-1));
return (x>>n)&mask;
測試,報錯 (不可用-號)(
改正
int logicalShift(int x, int n) {
int mask=~(((1<<31)>>n)<<1);
return (x>>n)&mask;
(理由:最後要左移一位,因為首位已經是1了,如果左移n位,會多出現一個1)
4.題目與解答
/*
* bitCount - returns count of number of 1's in word
* Examples: bitCount(5) = 2, bitCount(7) = 3
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 40
* Rating: 4
*/
int bitCount(int x) {
int result;
//int mask1=(0x55)|(0x55<<8)|(0x55<<16)|(0x55<<24);
int tmp_mask1=(0x55)|(0x55<<8);
int mask1=(tmp_mask1)|(tmp_mask1<<16);
//int mask2=(0x33)|(0x33<<8)|(0x33<<16)|(0x33<<24);
int tmp_mask2=(0x33)|(0x33<<8);
int mask2=(tmp_mask2)|(tmp_mask2<<16);
//int mask3=(0x0f)|(0x0f<<8)|(0x0f<<16)|(0x0f<<24);
int tmp_mask3=(0x0f)|(0x0f<<8);
int mask3=(tmp_mask3)|(tmp_mask3<<16);
int mask4=(0xff)|(0xff<<16);
int mask5=(0xff)|(0xff<<8);
//add every two bits
result=(x&mask1)+((x>>1)&mask1);
//add every four bits
result=(result&mask2)+((result>>2)&mask2);
//add every eight bits
//result=(result&mask3)+((result>>4)&mask3);
result=(result+(result>>4))&mask3;
//add every sixteen bits
//result=(result&mask4)+((result>>8)&mask4);
result=(result+(result>>8))&mask4;
//add every thirty two bits
//result=(result&mask5)+((result>>16)&mask5);
result=(result+(result>>16))&mask5;
return result;
}
題意解析:
計算一個數字在計算機儲存時,有多少個1
解答過程與思考:
首先,想到通過一個for迴圈
for(i=0;i<32;i++)
{if ((1<<i)&x))
count++;
}
來計算有多少個1
接下來就是轉化問題(自己實在想不到,然後看了下答案 )
發現答案也只是生成一群掩碼,然後按位與,並用變數儲存,最後返回的過程(二叉檢索)
5題目與解答:
/*
* bang - Compute !x without using !
* Examples: bang(3) = 0, bang(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int bang(int x) {
return ~ ((x|(~x+1))>>31)&0x1;
}
題意解析:
就是非運算
解答過程與思考:
在整個過程中,只有當x=0的時候,運算結果才為1,而其它均為0
那麼在這個過程中,需要判斷x是否等於0就可以了。
轉而,又想到是否可以通過x*x來判斷是否等於0(此路不通)
最後,又想到對於負數是可以通過符號位來判定其不等於0,那麼是否可以找到一個通式對x做一定的變換,來保證正數也能通過符號位來判定是否等於0
即 x|~x 這樣的話,x為正數時,也能通過其符號位來驗證
然而,對於這個算式,0也同時滿足這個條件
轉而 修正為 x|(~x+1)
然後 函式寫為
int bang(int x) {
return ((x|(~x+1))>>31)&0x1;
}
驗證,錯誤。(忘記取反)
修正函式
int bang(int x) {
return ~((x|(~x+1))>>31)&0x1;
}
6.題目與解答
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 1<<31;
}
題意解析:
這題非常簡單,只需要求出32位能表示的最小數即可
7.題目與解答
/*
* fitsBits - return 1 if x can be represented as an
* n-bit, two's complement integer.
* 1 <= n <= 32
* Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int fitsBits(int x, int n) {
int isNeg = x >> 31;
int shift = n + ~0;
return ((isNeg & !((~x) >> shift)) + (~isNeg & !(x >> shift)));
}
題意解析:
一開始怎麼也看不懂題目,後來諮詢了助教,終於明白題目的意思了。x能否被n位二進位制數完全表示
-4 =100 而5 無法表示
即
3位有符號數能表示的範圍是100~011,也就是-4~3,而5不在這個範圍內,即無法表示,結果為0;-4在這個範圍內,那麼結果則為1
解答過程與思考:
首先,如果x>=0的話,
對於3位bits 000 001 010 011
對於4位bits 0000 0001 0010 0011 0100 0101 0110 0111
發現關係 return (!(x>>(n-1)))
如果x<0
3位 100 101 110 111
4位bits 1000 1001 1010 1011 1100 1101 1110 1111
發現這些負數取反正好就是一群正數 那麼 對於負數 就可以用
!((~x)>>(n-1))來判定
所以 最後的函式應是
int isNeg = x >> 31;//正數為0 負數為1
int shift = n + ~0;
return ((isNeg & !((~x) >> shift)) + (~isNeg & !(x >> shift)));
8:題目與解答
/*
* divpwr2 - Compute x/(2^n), for 0 <= n <= 30
* Round toward zero
* Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int divpwr2(int x, int n) {
//all zeros or all ones
int signx=x>>31;
//int mask=(1<<n)+(-1);
int mask=(1<<n)+(~0);
int bias=signx&mask;
return (x+bias)>>n;
}
題意解析:
題目的意思就是返回 x /(2^n)
解答過程與思考:
首先考慮正數,只需要return x>>n 就滿足
再考慮負數 發現符號位產生干擾,可以排除符號位 再在最後補上
取符號位 signbit=(x>>31)<<31
消去符號位 temp=x<<1 然後邏輯右移n+1位
int divpwr2(int x, int n) {
int signbit=(x>>31)<<31;
int temp=(x<<1);
int mask=~((1<<31)>>n);
temp=((temp>>(n+1))&mask);//邏輯右移n+1
return (temp|signbit);
}
驗證,報錯
沒有考慮到-0的概念
修正函式
參考網路答案:
int divpwr2(int x, int n) {
//all zeros or all ones
int signx=x>>31;
//int mask=(1<<n)+(-1);
int mask=(1<<n)+(~0);
int bias=signx&mask;
return (x+bias)>>n;
}
它就是把負數變換成正數來處理。
它先通過signx來判斷正負
而對於負數 只需要通過~(~(x-1)>>n)+1來處理(負數補碼與正數補碼的關係)
對於該算式 =(x-1)>n+1=(x-1+1<<n)>>n 這就是它 最基本的意思
~(~(x-1)>>n)+1=(x-1)>>n+1=(x-1+(1<<n))>>n
0xFF FF FF FF =-1 0x00 00 00 01=1
其實 可以用
return (~(x>>31)&(x>>n))+((x>>31)&(~((~x+1)>>n)+1));
來計算
9:題目與解答
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x+1;
}
題意解析:
返回相反數
解答過程與思考:
在計算機中,數字以補碼形式儲存。所以,只需要 x=~x+1
10:題目與解答
/*
* isPositive - return 1 if x > 0, return 0 otherwise
* Example: isPositive(-1) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 8
* Rating: 3 xian qiu x<0 x>>31 dang x=0 !x
*/
int isPositive(int x) {
return !((x>>31)|!x);
}
題意解析:
當x大於0,返回1 其餘返回0
解答過程與思考:
首先,通過其符號位判定return !(x>>31)
發現,0不滿足這個式子
修正 return !((x>>31)|!x)
驗證成功
11:題目與解答
/*
* 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 sub=y+(~x+1);
int signx = (x >> 31) & 1;
int signy = (y >> 31) & 1;
int subsign = (sub >> 31) & 1;
return ((signx ^ signy) & signx) + ((signx ^ signy ^ 1) & !subsign);
}
題意解析:
x<=y? 1:0
解答過程與思考:
首先,可以通過計算t=y-x 來判定(y-x=y+(~x+1))
int isLessOrEqual(int x, int y) {
return !((y+(~x+1))>>31);
}
經過測試,發現忘記考慮計算溢位的情況(即如果X,Y符號不相同,可能出現溢位)
當x=[0x80 00 00 00] y=[0x7f ff ff ff] sub=[0xFF FF FF FF]
當x=[0x8F FF FF FF] y=[0x7f ff ff ff] sub=[0xF0 00 00 00]
當x=[0x7f ff ff ff] y=[0x80 00 00 00] sub=[0x00 00 00 01]
當x=[0x7f ff ff ff] y=[0x8F FF FF FF] sub=[0x10 00 00 00]
發現 溢位後的符號位 與減數符號相同
修正函式
int isLessOrEqual(int x, int y) {
int sub=y+(~x+1);
int signx = (x >> 31) & 1;
int signy = (y >> 31) & 1;
int subsign =!((sub >> 31) & 1);//正的話 1 負的話是0
return ((signx ^ signy) & signx) + ((signx ^ signy ^ 1) & subsign);
}
12:題目與解答
/*
* ilog2 - return floor(log base 2 of x), where x > 0
* Example: ilog2(16) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int ilog2(int x) {
int byte3 = x >> 24;
int byte2 = (x >> 16) & 0xff;
int byte1 = (x >> 8) & 0xff;
int byte0 = x & 0xff;
int i3 = !!byte3; /* 0: byte3=0, 1: byte3 > 0 */
int i2 = i3 | !!byte2;
int i1 = i2 | !!byte1;
int i0 = i1 | !!byte0;
int i = i3 + i2 + i1 + i0 + ~0;
int highbyte = x >> (i << 3); /* highest byte not equal zero */
int b7 = (highbyte >> 7) & 1;
int b6 = (highbyte >> 6) & 1;
int b5 = (highbyte >> 5) & 1;
int b4 = (highbyte >> 4) & 1;
int b3 = (highbyte >> 3) & 1;
int b2 = (highbyte >> 2) & 1;
int b1 = (highbyte >> 1) & 1;
int b0 = highbyte & 1;//
int k7 = b7;
int k6 = k7 | b6;
int k5 = k6 | b5;
int k4 = k5 | b4;
int k3 = k4 | b3;
int k2 = k3 | b2;
int k1 = k2 | b1;
int k0 = k1 | b0;
int k = k7 + k6 + k5 + k4 + k3 + k2 + k1 + k0 + ~0;
return (i << 3) + k;
}
題意解析:
就是log2x=n
解答過程與思考:
對於正數
16 0x00 00 00 10 =》0x00 00 00 04
15 0x00 00 00 0f =》0x00 00 00 03
額 這題實在不會 參考網路答案
它先劃分成4個8位數值 然後依次判斷大小,並儲存
通過i來判斷從高位數起第一個1在哪個位元組裡
如果i=0 就代表 最後8位 如果i=2就是24-16位 。。
b01234567來儲存取出的8位裡的相應位值
再用k來選最高位1是哪一位 從而得出n
13:題目與解答
/*
* float_neg - Return bit-level equivalent of expression -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 representations of
* single-precision floating point values.
* When argument is NaN, return argument.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 10
* Rating: 2
*/
unsigned float_neg(unsigned uf) {
if (~(((uf >> 23) & 0xff) ) || !(uf & ((1 << 23) - 1)))
uf ^= (1 << 31);
return uf;
}
題意解析:
如果uf是NaN 返回NaN 否則,返回-f 而且可以用if語句 那就非常簡單了
(不過這裡竟然出現了無符號float。。表示不大懂)
解答過程與思考:
0 1111 1111 0000 0000 0000 0000 0000 000
只要後面32位中有一位不是0就是NaN
首先判斷 是否是NaN
(((uf>>23)&0xff)==0xff)&&
((uf&(0000 0000 0111 1111 1111 1111 1111 1111))!=0) =>1
轉化=>
即 當uf不為NaN時,
~((((uf>>23)&0xff)==0xff)&&((uf&(0000 0000 0111 1111 1111 1111 1111 1111))!=0))
=>
(~((uf >> 23) & 0xff) ) || !(uf & ((1 << 23) - 1)) =》1//一個判斷 規格化 一個判斷非規格化特殊值情況
14:題目與解答
/*
* float_i2f - Return bit-level equivalent of expression (float) x
* Result is returned as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point values.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_i2f(int x) {
unsigned shiftLeft=0;
unsigned afterShift, tmp, flag;
unsigned absX=x;
unsigned sign=0;
//special case
if (x==0) return 0;
//if x < 0, sign = 1000...,abs_x = -x
if (x<0)
{
sign=0x80000000;
absX=-x;
}
afterShift=absX;
//count shift_left and after_shift
while (1)
{
tmp=afterShift;
afterShift<<=1;
shiftLeft++;
if (tmp & 0x80000000) break;
}
if ((afterShift & 0x01ff)>0x0100)
flag=1;
else if ((afterShift & 0x03ff)==0x0300)
flag=1;
else
flag=0;
return sign + (afterShift>>9) + ((159-shiftLeft)<<23) + flag;
}
題意解析:
就是一個強制轉換(float)x
解答過程與思考:
首先,考慮float與int在底層的區別
float 1 (1111 1111 )(1111 1111 1111 1111 1111 111)
int 1 111 1111 1111 1111 1111 1111 1111
顯然,要先用一個sign來儲存符號位
額,接下來就不會做了。
參考網路答案,它也首先儲存了符號位,然後對於正負進行處理,最後通過一個while迴圈與flag修正小數位和階碼位
flag考慮的是否進位
15:題目與解答
/*
* float_twice - 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 float_twice(unsigned uf) {
unsigned f=uf;
/* Computer 2*f. If f is a NaN, then return f. */
if ((f & 0x7F800000) == 0){
//shift one bit to left
f = ((f & 0x007FFFFF)<<1) | (0x80000000 & f);
}
else if ((f & 0x7F800000) != 0x7F800000){
/* Float has a special exponent. */
/* Increment exponent, effectively multiplying by 2. */
f =f+0x00800000;
}
return f;
}
題意解析:
返回兩倍float x,如果是NaN就返回NaN
解答過程與思考:
參考網路答案:
首先,考慮NaN與特殊值 x 1111 1111 xxxx xxxx xxxx xxxx xxxx xxx 因為傳入的是unsigned 所以 符號位為0
對於 特殊值 正無窮 0x7F 80 00 00
0111 1111 1000 0000 0000 0000 0000 0000
兩倍正無窮 還是返回正無窮
而又因為NaN返回本身
所以 只要階碼域全為1 則返回本身
(f&0x7F800000)==(0x7F800000) return uf
再考慮 非規格化數:uf=0000 0000 0xxx xxxx xxxx xxxx xxxx xxxx
2*uf = 這個數足夠大,最後能轉化為規格化數 那麼 只需要將小數位像階碼位進位(《《1) 如果不夠大,只需要講小數位<<1 這樣的話,就可以用通式 (f & 0x007FFFFF)<<1) 來計算 ((f&0x7F800000)==0) return (f & 0x007FFFFF)<<1)
再考慮 規格化數((f & 0x7F800000) != 0x7F800000) 因為這個IF語句在後邊,所以不用考慮 非規格化值
uf=0xxx xxxx x xxx xxxx xxxx xxxx xxxx xxxx
只需要在階碼域加1 即 f+0x00800000
最後,至於為什麼會有 | (0x80000000 & f); 這一句,好像是因為排除負數影響 可是,這不是傳入一個unsigned 數值麼? 這裡還不是很明白
如果刪去後面半句話,程式會報錯
相關文章
- csapp Lab1APP
- CSAPP:Lab1 -DataLab 超詳解APP
- 二進位制、位運算、位移運算
- (位運算)兩個字串的位運算字串
- 位運算
- 理解位運算
- SQL位運算SQL
- 使用位運算進行加法運算
- 學習位運算
- PHP 位運算使用PHP
- 位運算進階
- 位運算總結
- 巧用JS位運算JS
- 位運算-異或(^)
- java二進位制運算十進位制(精確運算)Java
- 運算整數C/C++位運算技巧C++
- 計算機基礎:位運算計算機
- JS中的位運算JS
- 位運算簡單操作
- Java位運算小節Java
- Java中的位運算Java
- 位運算的應用
- ~ 按位取反運算解析
- iOS 開發 – 位運算iOS
- 位運算子的計算
- C語言位運算C語言
- 【SSLOJ 3348】位運算
- 位運算與SQL實現SQL
- 【演算法】位運算技巧演算法
- 【每日演算法】位運算演算法
- 演算法之位運算演算法
- 處理器運算位數
- 按位取反運計算方法
- C# 位運算及例項計算C#
- 【演算法技巧】位運算裝逼指南 ---- 帶你領略位運算的魅力演算法
- 二進位制與二進位制運算
- 有趣的二進位制2—高效位運算
- 演算法之美 : 位運算演算法