任務:按照要求補充13個函數,會限制你能使用的操做及數量git
/* * 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))); // 直接推公式,^可使用~、&和|表示,而|又能夠用~和&表示 }
推導一下公式就能夠直接寫出來了。
github
/* * 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位來構造最小補碼。
express
/* * 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的干擾。
app
/* * 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。
less
/* * negate - return -x * Example: negate(1) = -1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ int negate(int x) { return ~x + 1; // 補碼取相反數操做:按位取反再加一 }
直接套用公式。
ide
/* * 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的不一樣來返回不一樣的值。
ui
/* * 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的大小。
code
/* * 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~1五、16~2三、24~2七、28~2九、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; // 最後加上符號位 }
註釋已經寫的很清楚了,能夠邊看代碼邊打草稿,很容易理解。
orm
/* * 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; }
全部代碼及相關實驗說明材料都在這裏