目录
实验准备
一些关于位操作
常见的二进制位操作
关于掩码
掩码是一个位模式,表示从一个字选出的位的集合
位级运算x&0xFF生成一个由x的最低有效字节组成的值,而其他的字节就别置为0
例如:
x = 0xABCDEF89
x&0xFF = 0x00000089
(若我们要获去更高位的字节,可以通过移位和该位级运算)↓
x>>8 & 0xFF = 0x0000EF
x>>16 & 0xFF = 0x00CD
x>>24 & 0xFF = 0xAB
一些关于浮点数
•符号(sign)s决定这数是负数(s=1)还是正数(s=0),而对于数值 0 的符号位解释作为特殊情况处理。
•尾数(signiticand) M是一个二进制小数,它的范围是 1~2-€,或者是 0~1-€。
•阶码(exponent) E的作用是对浮点数加权,这个权重是2的正次幂(可能是负数)。
将浮点数的位表示划分为三个字段,分别对这些值进行编码:
•一个单独的符号位s 直接编码符号s.
•k位的阶码字段exp=e0*...ek-1编码阶码 E。(编码E不是说exp=E)
•n位小数宇段frac=f0*...fn-1编码尾数 M,但是编码出来的值也依赖于阶码宇段的值是否等于 0。
根据exp的值被编码的值可以分为三种不同情况
情况一:规格化的值
•exp的位模式不全为0,也不全为1
•此时E=e-bias(单精度bias=127,双精度bias=1023)
•frac是小数点右边那一串,此时M=1+frac
情况二:非规格化的值
•exp的位模式全为0
•此时E=1-bias
•M=frac
情况三:特殊值(无穷大或NaN)
•exp的位模式全为1
•无穷大frac位模式全为0
•NaN的frac位模式不全为0
例如:
15213(10)= 11101101101101(2)
= 1.1101101101101(2)* 2^ 13
(把15213转化成规格化浮点表示)
•M=1.1101101101101
•E=13 -> exp = e+127=140 -> exp = 10001100(2)
所以15213(10)的浮点数表示为
题目题解
bitXor(x,y)
只使用两种位运算实现异或操作
int bitXor(int x, int y) {
return ~(~x&~y)&~(x&y);
}
题解:
列异或真值表,得(x|y)&~(x&y),和答案等价
tmin()
返回二进制最小数
int tmin(void) {
return 0x1<<31;
}
题解:
int是32位,int_min = 0x80000000等价于答案
isTmax(x)
通过位运算计算是否是补码最大值
int isTmax(int x) {
int tmin = x + 1;
x = x + tmin;
x = ~x;
tmin = !tmin;
x = x + tmin;
return !x;
}
题解:
如果x是tmax(0111...)则tmin=x+1(1000...),x=x+tmin = 111...1,将按位取反得到x=000...0,但是注意-1(0xFFFFFFFF)也符合该转化,所以要排除,此时tmin=0取反为1,x+!tmin 不等于0则可以排除-x为-1的情况
allOddBits(x)
判断所有奇数位是否都为1
int isTmax(int x) {
int tmin = x + 1;
x = x + tmin;
x = ~x;
tmin = !tmin;
x = x + tmin;
return !x;
}
题解:
用掩码解决,可以参考上文。0x55二进制表示为(01010101),c << 8的二进制表示为(0101010100000000)第一步c的二进制表示为(0101010101010101)以此类推。。。然后通过掩码得到的c和x做与运算获得x的奇数位,其他位为0,再与c异或若为0则奇数位全为1,所以将结果在进行逻辑取反
negate(x)
不使用 - 操作符,求 -x 值
int negate(int x) {
return ~x+1;
}
题解:
可以自己举例子-1和1的例子试试~
isAsciiDigit(x)
计算输入值是否是数字 0-9 的 ASCII 值
int isAsciiDigit(int x) {
int a = !(x >> 4 ^ 0x3);
int b = !((x & 0xA) ^ 0xA);
int c = !((x & 0xC) ^ 0xC);
return a & !(b | c);
}
题解:
只要高四位等于三,低四位在0-9则符合题意,右移四位获得高四位异或0x3取反,因为是0-9则(1001)和(1010)不符合题意,x分别与0xA和0xC做与运算获得他的位,在进行异或取反
conditional(x, y, z)
使用位级运算实现C语言中的 x?y:z三目运算符
int conditional(int x, int y, int z) {
x = !!x;//第一步
x = ~x+1;//第二步
return (x&y)|(~x&z);
}
题解:
第一二步就是把非0的位表示转化为全0,0的位表示为全0.如果x为非0经过第一步其位表示位(000..01),再经过第二步转化为(111...11),如果x为0经过第二步(1111...1+1 -> 100..00)最高位舍去,位表示全为0
isLessOrEqual(x,y)
使用位级运算符实现<=
int isLessOrEqual(int x, int y) {
int a = ~x + 1;
int b = y + a;//获得y-x
int tmin = 1 << 31;
int xsign = !!(x & tmin);//取到符号位,在进行!!可以得1或0
int ysign = !!(y & tmin);
int zsign = b & tmin;//获得y-x的符号位
return (!(xsign ^ ysign) & !zsign) | ((xsign ^ ysign) & xsign);
}
题解:
分符号相同与不同,符号为相同时要满足y-x>=0,符号不同时要满足x<0。
logicalNeg(x)
使用位级运算求逻辑非 !
int logicalNeg(int x) {
int c = ~x + 1;
return ((x | c) >> 31) + 1;
}
题解:
0和最小数的补码是本身,其他数的补码是相反数
howManyBits(x)
一个数用补码表示最少需要几位
int bit16,bit8,bit4,bit2,bit1,bit0;
int sign=x>>31;
x = (sign&~x)|(~sign&x);//如果x为正则不变,否则按位取反(这样才能进行下面运算,
//利用高位是否存在1,将x进行移位运算
bit16 = !!(x>>16)<<4;//高十六位是否有1,若存在1则bit16=16 二进制表示(10000)
x = x>>bit16;//如果有(至少需要16位),则将x右移16位
bit8 = !!(x>>8)<<3;//剩余16位的高8位是否有1,若存在1则bit8=8 二进制表示(1000)
x = x>>bit8;//如果有(至少需要16+8=24位),则右移8位
bit4 = !!(x>>4)<<2;//同理
x = x>>bit4;
bit2 = !!(x>>2)<<1;
x = x>>bit2;
bit1 = !!(x>>1);
x = x>>bit1;
bit0 = x;
return bit16+bit8+bit4+bit2+bit1+bit0+1;//+1表示加上符号位
题解:
正数在计算机中以原码存储,负数以补码存储,则正数的求法是找到最高位1存在位置加符号位,负数的求法本来是找到最高位0存在位置加符号位
floatScale2(f)
求2乘一个浮点数
unsigned floatScale2(unsigned uf) {
unsigned exp=(uf>>23)& 0xff;//先右移23位再取8位获得exp位模式
unsigned frac = uf & 0x7fffff;//取23位获得frac位模式
unsigned ans;
if (exp==0xff)//特殊情况(无穷大或NaN)
ans=uf;
else if(exp)//规格化exp位模式不全为0,2的指数E=exp-bias,所以exp++相当于*2
exp++;
else//非规格化exp位模式全为0,E=1-bias,M=frac 根据浮点数表示M*2相当于frac<<1
frac<<=1;
ans= (uf&0x80000000) | (exp<<23) | frac;
return ans;
}
题解:
先获得exp和frac的位模式,对三种情况进行分析,然后和0x80000000做与运算获得符号位,将exp左移23位(ek-1,ek-2,...,e0,000...0),再将三者相或,位为1的将会保留,从而得到浮点数的位表示
floatFloat2Int(f)
将浮点数转换为整数
int floatFloat2Int(unsigned uf) {
unsigned s=uf>>31;
unsigned exp = (uf>>23) & 0xff;
unsigned frac = uf & 0x7fffff;
int E = exp-127;
if(exp == 0 && frac == 0)//0的情况
return 0;
if(exp == 0xff){//特殊情况
return 1<<31;
}
if(exp == 0){//非规格化,M<1,E=1-127=-126,值很小
return 0;
}
//规格化
frac = frac|(1<<23);//此时frac等于M
if(E>31)//溢出
return 1<<31;
else if(E<0){
return 0;//1<M<2,E最小为1/2,所以值小于1
}
if(E>23){
frac<<=(E-23);//如果E大于23,frac可以左移则右边有更多的0位
}else{
frac>>=(23-E);//如果E小于23,则要右移,否则frac会多出位
}
if(s)
return ~frac+1;
else
return frac;
}
题解:
规格化时,frac位数是23,E很大时,frac的位都能保留,还能多给0位(不变),所以将frac左移,E小于23时,frac要裁掉后面的无效位,才能满足M*2^E(M=1.xxxx),正数返回frac,负数用补码存储,所以返回frac的补码
floatPower2(x)
求 2.0^x
unsigned floatPower2(int x) {
if(x<-149){
return 0;
}else if(x<-126){//非规格
//E = x = 1-127 = -126
int move = 23+(x+126);
return 1<<move;
}else if(x<127){
int exp = x+127;//E = exp-127
return exp<<23;
}else{
return (0xff)<<23;
}
}
题解:
2.0^x是非负数,所以可以表示为M^2E。找到非规格化和规格化的表示范围。非规格化时,M最小为2^-23(000...001),E=-126,所以非规格化最小是M^2E=2^-149。M最大表示(111...11),E=-126,所以非规格化最大是M^2E=2^-126(取不到)。
规格化时,M最小为1.0,E最小等于1-127 = -126(因为规格化时exp不能全0,最小位表示[00000001])
所以规格化最小是M^2E=2^-126。M尽可能大1.111...1,E最大254-127=127(exp位表示11111110),M最大小于2,所以规格化最大在2^127~2^128.
当范围在非规格化时2.0^x = M*2^E,此时2^E=2*-126所以M需要补充(x+126)需要的位,只要返回M的位模式就是最终答案,frac有23位,令move=23+(x+126)可以得出要移动的位数,最后返回1左移需要移动的位数。
总结
好好理解实验准备,可以画画草稿
要是有错误求求大佬指出,小白发滴第一篇博客嘿嘿嘿!~