DataLab
可以总结很多,要回顾
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) {
return ~(x&y)&~(~x&~y);
}
tmin
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 0x1<<31;
}
isTmax
题目:通过位运算计算是否是补码最大值
思考:
- tmax与一般数值的不同之处在于,再往上加就会溢出(5位bit为例),是否有什么特别的呢?
- tmax : 01111 + 1 得到 10000(第一个1权值为-16) (15变-16)
- ~tmax=10000
- 故~tmax=tmax+1
- 可验证-1(11111)也有此性质,其余无
- 以上结论也可由-tmin=~tmin+1=tmin性质推导出来
- 故解决办法:用异或判断~x和x+1是否相等,并且排除-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) {
return !( (x+1)^(~x) ) & (!!(~x));
}
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: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 2
*/
int allOddBits(int x) {
// return !(~x&0xaaaaaaaa);不能用大常量
int t=(0xaa<<24)+(0xaa<<16)+(0xaa<<8)+(0xaa);//自己构造0xaaaaaaaa
return !(~x&t);
}
negate
题目:求 -x 值
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x+0x1;
}
isAsciiDigit
题目:计算输入值是否是数字 0-9 的 ASCII 值
思路:
1.若在其中,必定满足x -0x30>=0且x-0x3a<0
2.根据1.判断符号位即可
//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) {
return !((x+(~0x30+1))>>31)&((x+(~0x3a+1))>>31);
}
conditional
题目:使用位级运算实现 C 语言中的 x?y:z 三目运算符
思考:
- 怎么区分0和非0数?
- !x 即可,得到0x01或者0x00
- 要变成0x0000000和0xffffffffff如何操作?
- 法1:!x-1 即 !x+(~1+1)
- 法2:!x<<31>>31
- 上面两种方法可以在0,1,全1,全0中变换,很实用
- 若想全0或全1,变成1,0,则+1即可,0xffffff溢出为0,0x00000变成1
/*
* 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) {
//return (!x+(~1+1))&y|~(!x+(~1+1))&z;
return (!x<<31>>31)&z | ~(!x<<31>>31)&y;
}
isLessOrEqual
使用位级运算符实现 <=
思路:
- y-x>=0判断符号位
- 若2者异号,相减会出现相加情况(相当于两个正数或者两个负数相加),可能出现溢出,不适合通过结果的符号位判断
- 故若二者异号,直接判断 x<0,返回1,x>0,返回0
/*
* 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 isSameSign= !( ((x^y)>>31) & 0x1 );//&0x1将不需要的位置0,以免影响结果
int e= (( y+(~x+1) ) >> 31) & 0x1;
return ( isSameSign & !e ) | ( !isSameSign & ( (x>>31)&0x1) );
}
若改成
int e= (( x+(~y+1) ) >> 31) & 0x1;
需要多一步判断x等于y的情况
logicalNeg
题目:使用位级运算求逻辑非 !
思路:
- 区别0和非0数值的特征是什么
- +0=-0 符号位相同,tmin=-tmin
- 其余+x 和 -x 符号位不同
- 故解决办法:通过或运算,判断符号位,不通过异或是因为tmin的存在,只有0和-0符号位 或 为0
- 判断该符号位,若为0返回1,若为1返回0(通过>>31+1实现)
//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) {
return return (((~x+1)|x)>>31) +1 ;//>>31变成全0或全1,+1后,0变1,全1溢出为0,返回
}
howManyBits
题目:“一个数用补码表示最少需要几位?”
思考:
- 怎么确定补码表示该数的位数?关键点在于?
- 负数:第一个0的位置+1,取反变成找第一个1的位置
- 正数:第一个1的位置+1
- 前16位是否有1?若有,起码需要16位,故去掉后16位(右移16),若没有,不移位
- 考察前8位是否有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 b16,b8,b4,b2,b1,b0;
int sign=x>>31;
x=sign&(~x) | (~sign)&x;//如果x为正则不变,否则按位取反(统一找最高位为1)
b16=!!(x>>16)<<4;//判断前16位是否有1,并且同时计算了需要移位的位数
x=x>>b16;//有1就移动16位,否则b16=0,不移动
b8=!!(x>>8)<<3;
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;
}
floatScale2
题目:求 2 乘一个浮点数
思路:
- 若为非规格化数,exp全0,只能通过frac左移一位变成2倍(见下图)
- 若为规格化数,frac有隐藏位,无法移动,可通过exp+1来变成2倍
- 若为NaN,无穷,返回参数(变2倍之前)
- 变2倍后变成无穷,返回无穷
//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) {
//分别取符号位,尾数,指数
int sign=uf&(1<<31);
int frac=uf&0x007fffff;
int exp=(uf>>23)&0xff;
//非规格化数,指数全0,尾数无隐藏位,故可通过左移一位变为2倍,考察临界值(非规格化最大数),也成立,见下图绿色部分
if(exp==0) return uf<<1|sign;
//NaN,无穷大
if(exp==255) return uf;
//规格化数,有隐藏位,故通过改变指数变成2倍
exp++;
if(exp==255) return 0x7f800000|sign;
return exp<<23|frac|sign;
}
floatFloat2Int
题目:将浮点数转换为整数
思路:
- 分别取出s,exp,frac
- 补充frac的隐藏位1,得到完整尾数m
- exp-bias得到指数e,根据指数e来移位
- e<0,小数点左移,一定为小数,舍入为0
- e>31,小数点右移,最高位溢出,返回给定溢出值tmin
- 小数点后有23位,根据e与23大小关系,正确移位
- 正确移位完成得到整数M,此为数值大小,再进行符号判断
- 若M最高位跟原S符号相同直接返回
- 若不同,且M最高位为1,则用现有位无法表示(需要额外一个位来表示符号)
- 若不同,且M最高位为0,则返回-M即(~M+1)
/*
* 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 tmin=0x1<<31;//溢出需返回的值
int sign=uf>>31;//取符号
int frac=uf&0x007fffff;//取尾数
int e=((uf>>23)&0xff)-127;//取指数 exp-偏置( 2^7 -1)=127
int M = frac|(0x1<<23);//补充隐藏位1
if(e<0) return 0; //如果指数小于0,相当于尾数小数点左移,必定为小数,舍入为0
if(e>31) return tmin;//如果指数大于31,相当于尾数小数点右移31,溢出
if(e>23) M=M<<(e-23);//如果指数>23,小数点左移e-23
else M=M>>(23-e); //否则右移23-e
if(!( (M>>31) ^sign)) return M;//如果移动完成的尾数M符号位与sign相同,无需转换,直接返回M
else if(M>>31) return tmin;//如果不同,且M为负数,则无法表示(需更多位),返回溢出
else return ~M+1; //如果不同,且M为正数,返回其相反数
}
floatPower2
思路:
求2.0x,即1*2.0x,转为浮点表示
s=0,e=x,frac=0
假设为规格化数:exp=e+bias=x-127,考察e范围,根据e判断返回值
梳理:浮点表示中,用exp来编码指数e,exp是无符号数,范围为1~+254(规格化范围),故指数e的范围为-126 ~+ 127,
若e<-126 ,exp<1,为非规格化数,且frac全0,故为0
若e>127,exp>254,全1,为非规格化数,且frac全0,s为0,故为+INF
见上图比较清晰
/*
* 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=0x7f800000;
if(x<-126) return 0;
if(x>127) return inf;
return (x+127)<<23;
}