考試周除了學習什么都好玩,偶然發現了B站上的“精翻”視頻,就沖了
第一章的視頻還沒看完(太長了quq),這里也只是寫了整形的lab,寫了大概有一整天
明天烤完高代就滾回來填這個lab、課程筆記、導論4、集合論習題的坑...好像有點多,不管了
這些只在本地btest過,不保證能對...如果有錯或者有更好的做法歡迎指正!
upd:做完了,爽耶
tricks
-
\([a=b]\iff [(a \otimes b)=0]\iff [(a-b)=0]\)
這個視能否使用"-"和"^"來選擇,相當於不用if做出了判斷是否相等 -
\((111\dots 11)_2=(-1)_{10}\) 這個...沒啥好說的
-
\(f(flag,x)=\left\{{\begin{aligned}0,flag=0\\x,flag=1\end{aligned}}\right.\iff x\&(-flag)\),結合2就可以理解,結合4很有用
-
\((-x)=( (\sim x) + 1)\),這個實際上就是電路中減法的做法,這里可以看出反碼在簡化運算中的作用
bitXor
根據集合論/數理邏輯的知識可以很快想到異或的"對稱差"定義
//1
/*
* bitXor - x^y using only ~ and &
* Example: bitXor(4, 5) = 1
* Legal ops: ~ &
* Max ops: 14
* Rating: 1
*/
int bitXor(int x, int y) {
int fx = ~x, fy = ~y;
int tx = fx & y, ty = fy & x;
return ~((~tx) & (~ty));
}
tmin
這里的最小指的是補碼對應數值最小...這個直接符號位填1就好了
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return (1 << 31);
}
isTmax
tmax的特點是除了符號位都是1,那么加上1就得到了tmin,取反仍然是tmax
但是除了tmax還有別的數有這個性質:-1,排除掉就好了
//2
/*
* 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 t = x + 1;
return !( ( (~t) ^ x) | (!t));
}
allOddBits
lab有要求不能使用超過255的常量,那么一個想法就是把32bits分成4*8bits,
我們造一個(10101010)來復制4份就可以到奇數位全為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 t = 170 | (170 << 8);
t = t | (t << 16);
return !((t & x) ^ t);
}
negate
看trick4
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return (~x) + 1;
}
isAsciiDigit
這道題就比較靈性...
觀察一下asciiDigit的特點,最后6位都形如\((11xxxx)_2\),而最后4位恰好是\((0000)_2\sim (1001)_2\)
我最早的做法是把后6位摳出來,用倒數第4位判掉0~8的情況,再判掉最后2位的情況
事實上做了后面的isLessOrEqual就可以發現這里的另一種做法了...不是很懂這個順序啊
//3
/*
* 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 >> 4) ^ 3);
int B = !(x & 8);
int C = !(x & 6);
return A & ( B | C );
}
conditional
利用trick3就可以做了,構造\(f(flag,x)\otimes f(!flag,y)\)就好了
我在寫到這里的時候沒有意識到trick4可以用,所以寫的比較繁瑣
/*
* 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 px1 = !x;
int px2 = !px1;
int ty = ( (px2 << 31) >> 31 ) & y;
int tz = ( (px1 << 31) >> 31 ) & z;
return ty ^ tz;
}
isLessOrEqualTo
最直觀就是做差,判斷\(\triangle\)的符號位
然而當兩個數異號的時候,他們的差會溢出,為了處理這種狀況我們要先判掉異號的情況,這樣同號運算就是在范圍內的了
/*
* 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 d = x + (1 + (~y) );
int fx = (x >> 31) & 1;
int fy = (y >> 31) & 1;
return ( (!d) | ( (d >> 31) & 1) | ( fx & (!fy) ) ) & !( (!fx) & fy);
}
logicalNeg
可以發現符號位不重要,第一步先去掉符號位得到"絕對值"
如果是0的話取反就會得到-1,否則都得不到-1
此時加1又可以得到0,即符號位為正,而其余情況得到的都是負數
這個性質可以判掉"大部分"非0數字,特例是-2147483648,它沒有絕對值(或者說,"絕對值"是0)...所以特判一下就好了
//4
/*
* 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) {
int tx = (~x) | (1 << 31);
return ( ~( ( ( tx + 1 ) | x ) >> 31 ) ) & 1;
}
howManyBits
先考慮正數,我們要找的就是最高位的1在哪(第幾位)
負數的情況比較特殊,因為從符號位開始連續的1序列和單獨的一個符號位1等價(回憶課堂上的Sign Extension),那么我們只需要保留一個符號位,也就是只需要找到最高位的0就可以了
於是負數就取反,找最高位的1可以用二分(魔幻吧),想了好久才想到...
先判斷前16位是否有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) {
int t1, L1, t2, L2, t3, L3, t4, L4, t5, L5;
int s = ~1 + 1, rx = x, cx = !x;
int p = (x >> 31) & 1, dx = !(rx ^ s);
x ^= ~p + 1;
t1 = s & (x >> 16); L1 = ( (!!t1) << 4); x >>= L1;
t2 = s & (x >> 8); L2 = ( (!!t2) << 3); x >>= L2;
t3 = (x >> 4) & s; L3 = ( (!!t3) << 2); x >>= L3;
t4 = (x >> 2) & s; L4 = ( (!!t4) << 1); x >>= L4;
t5 = (x >> 1) & s; L5 = (!!t5); x >>= L5;
return L1 + L2 + L3 + L4 + L5 + 2 + (1 + ~cx ) + (1 + ~dx);
}
floatScale2
浮點數的編碼很有意思
分類討論。首先判掉NaN和INF,對於denorm的形式我們只要左移frac部分,對於norm形式我們只需要增加指數exp(why?)
這個例子大概是給你熟悉浮點數編碼分類的
//float
/*
* 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) {
unsigned s = (uf >> 31) & 1;
unsigned e = (uf >> 23) & 255;
unsigned m = uf & 8388607;
if (e == 0) {
m = m * 2;
} else if (e != 255) {
e = e + 1;
}
return (s << 31) | (e << 23) | m;
}
floatFloat2Int
試了一下,C里面的強制類型轉換會截掉小數點后的部分,除非某種類似1.9999999999999999999
的例子,在這個例子下類型轉換會變成2(why?)
事實上第二種情況我們不需要考慮,因此只需要把frac部分摳出來,前面添上1,按照exp-bias得到的指數位e偏移即可。很顯然如果它是一個denorm/指數為負的norm的話答案就是0
/*
* 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 s = (uf >> 31) & 1;
int e = (uf >> 23) & 255;
int m = uf & 8388607;
int bias = 127, i = 22, r = 1;
if (e == 255) {
return 0x80000000u;
} else if (e == 0) {
return 0;
} else {
e -= bias;
if (e < 0) return 0;
for (; i >= 0; i --) {
if ( (m >> i) & 1) break;
}
while (e > 0) {
e -= 1; i -= 1; r <<= 1;
if (i > 0) r |= ( (m >> i) & 1);
if (r < 0) return 0x80000000u;
}
return s?(-r):r;
}
}
floatPower2
這個也挺簡單的...
從這個題可以看出單精度(32位)浮點數能表示的數字的范圍
最大值是norm形式,exp為254(再大就是NaN和INF了),frac的每一位全為1(雖然在這題里不是這樣),就能得到\({\left(2-\epsilon\right)}^{127}\)
最小值是denorm形式,exp為0,frac為1,此時指數e是1-127,尾數額外提供了23位的指數,這樣就得到\(2^{-149}\)
這樣直接做就可以了
/*
* 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 s = 0, m = 0, e = 127;
if (x < 0) {
if (-x > 149) return 0;
else if (-x <= 126) {
e = 1;
} else {
e = 0;
m = (0x400000u) >> (-x - 126);
}
} else if (x > 0) {
if (x + 127 > 255) return 0x7f800000;
else e = x + 127;
}
return (s << 31) | (e << 23) | m;
}