#! https://zhuanlan.zhihu.com/p/661117391
datalab
datalab还是很难的,发出来一是为了方便以后复习,而是为了帮助有需要的同学们。希望看到这篇文章的同学不要直接抄袭,还是要自己思考。
一、实验目的
本实验目的是加强学生对位级运算的理解及熟练使用的能力。
二、报告要求
本报告要求学生把实验中实现的所有函数逐一进行分析说明,写出实现的依据,也就是推理过程,可以是一个简单的数学证明,也可以是代码分析,根据实现中你的想法不同而异。
三、函数分析
- bitNor函数
函数要求:
函数名 | bitNor |
---|---|
参数 | int , int |
功能实现 | ~(x|y) |
要求 | 只能使用 ~ 和 & 运算符,将结果返回。 |
实现分析:
实际上实现了x,y每一位存在1取0,全0取1。而&实现的是每一位全1取1,存在0取0。x,y先取反,再&得到全0取1,存在1取0。
函数实现:
int bitNor(int x, int y) {
return (~x)&(~y);
}
- copyLSB函数
函数要求:
函数名 | copyLSB |
---|---|
参数 | int |
功能实现 | set all bits of result to least significant bit of x |
要求 | ! ~ & ^ | + << >> |
分析:
int是有符号数,最低位左移到最高位,再右移回来。
函数实现:
int copyLSB(int x) {
return (x<<31)>>31;
}
- isEqual函数
函数要求:
函数名 | isEqual |
---|---|
参数 | int int |
功能实现 | return 1 if x==y,and 0 otherwise |
要求 | ! ~ & ^ | + << >> |
分析:
相等的话^得到全0,!后得到1
函数实现:
int isEqual(int x, int y) {
return !(x^y);
}
- bitmask函数
函数要求:
函数名 | bitmask |
---|---|
参数 | int int |
功能实现 | generate a mask consisting of all 1’s |
要求 | ! ~ & ^ | + << >> |
分析:
写写画画发现多位1异或就可以出来,关键是highbit左移的合法性,当highbit=31的时候<<31+1非法操作,不同编译器不同结果,所以要两次左移。
函数实现:
int bitMask(int highbit, int lowbit) {
int high=(1<<highbit<<1)+(~0);
int low=(1<<lowbit)+(~0);
int ans=high^low;
ans=(~((highbit+((~lowbit)+1))>>31))&ans;
return ans;
}
- bitCount函数
函数要求:
函数名 | bitCount |
---|---|
参数 | int |
功能实现 | returns count of number of 1’s in word |
要求 | ! ~ & ^ | + << >> |
分析:
对于两bit的数来讲,1的数量就等于x&1+x>>1&1。那么一字节中01010101就可以作为一个累加的掩码。00110011作为4位累加的掩码。00001111作为8位累加的掩码。递增到4字节,则是扩大4倍。01010101010101010101010101010101,00110011001100110011001100110011,00001111000011110000111100001111,00000000111111110000000011111111,00000000000000001111111111111111。
喜欢我强而有力的二进制直写吗:)
函数实现:
int bitCount(int x) {
int mask1=0x55;
int mask2=0x33;
int mask3=0x0F;
int mask4= (0xFF<<16)|0xFF;//0x00FF00FF;
int mask5= (0xFF<<8)|0xFF; //0x0000FFFF;
mask1=(mask1<<8)|mask1;
mask1=(mask1<<16)|mask1;//0x55555555
mask2=(mask2<<8)|mask2;
mask2=(mask2<<16)|mask2; //0x33333333;
mask3=(mask3<<8)|mask3;
mask3=(mask3<<16)|mask3;//0x0F0F0F0F;
x=(x&mask1)+((x>>1)&mask1);
x=(x&mask2)+((x>>2)&mask2);
x=(x&mask3)+((x>>4)&mask3);
x=(x&mask4)+((x>>8)&mask4);
x=(x&mask5)+((x>>16)&mask5);
return x;
}
- tmax函数
函数要求:
函数名 | tmax |
---|---|
参数 | void |
功能实现 | return maximum two’s complement integer |
要求 | ! ~ & ^ | + << >> |
分析:
tmax二进制是0后面31个1,1左移到首位再取反即可。
函数实现:
int tmax(void) {
return ~(1<<31);
}
- isNonNegative函数
函数要求:
函数名 | isNonNegative |
---|---|
参数 | int |
功能实现 | return1,ifx>=0,return 0 otherwise |
要求 | ! ~ & ^ | + << >> |
分析:
x>=0时,符号位为0,>>31位高位补0最后得到0,逻辑取反!即可。x<0时,符号位为1,>>31位后得到全1,逻辑取反!得到0。
函数实现:
int isNonNegative(int x) {
return !(x>>31);
}
- addOK函数
函数要求:
函数名 | addOK |
---|---|
参数 | int int |
功能实现 | Determine if can compute x+y without overflow |
要求 | ! ~ & ^ | + << >> |
分析:
正溢出负溢出后3个符号位为001或者110,而不溢出有以下几种情况:101,100,111,000,011,010。而001和110满足一个条件,前两位相同,并且和最后一位不同。在可以使用if的情况下直接判断,而在lab中,就要用其他方法判断:设符号位分别为a,b,c,则只需要返回!(((!(ab))&(ac))&(b^c))。
函数实现:
int addOK(int x, int y) {
int z=x+y;
int a=(x>>31)&0x1;
int b=(y>>31)&0x1;
int c=(z>>31)&0x1;
return !(((!(a^b))&(a^c))&(b^c));
}
- rempwr2函数
函数要求:
函数名 | rempwr2 |
---|---|
参数 | int int |
功能实现 | Compute x%(2^n),for 0<=n <=30 |
要求 | ! ~ & ^ | + << >> Negative arguments should yield negative remainders |
分析:
对于正数,最低n位就是答案。负数就是转换成正数再取最低n位再取反就是答案。要进行两次有关符号位的正反转换。又知道全1得到全反,全0得到本身,则有下面的函数。
函数实现:
int rempwr2(int x, int n) {
int s=x>>31;//x为正数就是全0,x为负数就是全1
int mask;
int ans;
x=(x^s)+(s&0x1);//x为负数的时候就是按位取反+1;
mask=(1<<n)+(~0);
ans=((x&mask)^s)+(s&0x1);//同上
return ans;
}
- isLess函数
函数要求:
函数名 | isLess |
---|---|
参数 | int int |
功能实现 | if x < y then return 1, else return 0 |
要求 | ! ~ & ^ | + << >> |
分析:
不考虑溢出的情况x < y得到x-y<0。只有负数-正数的情况下会产生溢出,得到正数。不溢出的情况下符号位有001,101,111,溢出符号位是100。 而x>=y时符号位有110,010,000,溢出时011。则在前4种情况下返回1,后4种情况下返回0。观察发现x符号位小于y符号位的时候直接返回1,剩下情况讨论相与。如果不限制ops可以像数字逻辑一样析取合取。
函数实现:
int isLess(int x, int y) {
int s = ((x&~y)>>31)&1;
int m = ~((x^y)>>31);
return s | (((m)&((x+~y+1))>>31)&1);
}
- absval函数
函数要求:
函数名 | absval |
---|---|
参数 | int |
功能实现 | absolute value of x |
要求 | ! ~ & ^ | + << >> |
分析:
正数取自己,负数用符号位操作,详细见9.。TMIN得到自己本身,没办法,int表示范围有限。
函数实现:
int absVal(int x) {
int s=x>>31;
x=(x^s)+(s&0x01);
return x;
}
- isPower2函数
函数要求:
函数名 | isPower2 |
---|---|
参数 | int |
功能实现 | returns 1 if x is a power of 2,and 0 otherwise,no negative number is a power of 2.0 is not a power of 2. |
要求 | ! ~ & ^ | + << >> ops 20 |
分析:
正数2的幂的话只有1个1,比如1000,-1得到0111,按位与就是全0,不会有同位出现。负数的话INT_MIN-1得到INT_MAX,按位与也是全0,要特判,直接特判负数。0-1得到全1,按位与也是0,要特判。
函数实现:
int isPower2(int x) {
return (!(x&(x+(~0))))&(!(x>>31))&((!!(x&x)));
}
- float_neg函数
函数要求:
函数名 | float_neg |
---|---|
参数 | unsigned |
功能实现 | Return bit-level equivalent of expression -f for floating point argument f |
要求 | Any integer/unsigned operations incl. ||, &&. also if, while Max ops: 10 |
分析:
float的参数是1,8,23。正负靠最高位判定。如果argument是NaN,即中间8位全1,末尾23位非全0.这个数unsigned大于首位+全1+全0,直接特判。
函数实现:
unsigned float_neg(unsigned uf) {
unsigned s= uf>>31;
if(s){
if(uf>0xFF800000)
return uf;
return uf&0x7FFFFFFF;
}
else{
if(uf>0x7F800000)
return uf;
return uf|0x80000000;
}
}
- float_half函数
函数要求:
函数名 | float_half |
---|---|
参数 | unsigned |
功能实现 | Return bit-level equivalent of expression 0.5*f for floating point argument f |
要求 | Any integer/unsigned operations incl. ||, &&. also if, while Max ops: 30 |
分析:
规格化的情况下直接E-1,当E=1的时候表示s*1.M*2^(-126),s*0.1M*2^(-125)
,此时除以2,得到s*0.1M*2^(-126)
,用S全0(1(M>>1))表示。非规格E全1的时候,直接返回。非规格E全0的时候表示0.M*2^(-126)
,除以2得到0.0M*2^(-126)
,用S全0(0(M>>1))表示。
函数实现:
unsigned float_half(unsigned uf) {
unsigned S=uf>>31;
unsigned E=(uf>>23)&0xFF;
unsigned M=uf&0x7FFFFF;
unsigned s=S<<31;
unsigned MS=M>>1;
unsigned m;
if(E==0x000000FF)
return uf;
if ((M&0x3)==3) {
m = (MS) +1;
} else {
m = MS;
}
if (E == 0) {
return (s) + m;
}
if(E==1)
return (s)+0x00400000+m;
return (s)+((E-1)<<23)+M;
}
- float_i2f函数
函数要求:
函数名 | float_i2f |
---|---|
参数 | int |
功能实现 | return bit-level equivalent of expression (float)×,result is returned as unsigned int. |
要求 | Any integer/unsigned operations incl. ||, &&. also if, while Max ops: 30 |
分析:
如果输入整数为0,直接返回0。如果输入整数为INT_MIN,返回0xCF000000,
处理符号位和无符号整数:首先确定符号位S,然后将输入整数取绝对值并赋给无符号整数ux。
关键在于向右进位舍入,当丢掉的大于一半,或者等于一半且低位为1的时候进,其余直接舍。
函数实现:
unsigned float_i2f (unsigned int x) {
// 提取符号位
unsigned int S = x & (1 << 31);
// 提取指数部分
unsigned int E = 0;
// 提取尾数部分
unsigned int M = 0;
// 用于四舍五入的临时变量
unsigned int round = 0;
// 计算绝对值
unsigned int Abs = S ? (~x + 1) : x;
unsigned int temp = Abs;
// 计算指数
while ((temp = temp >> 1))
++E;
// 计算尾数,左移保留精度位数
M = Abs << (31 - E) << 1;
// 计算四舍五入位
round = M << 23 >> 23;
// 移除尾数多余的位数
M = M >> 9;
// 进行四舍五入
if (round > 0xFF + 1)
round = 1;
else if (round < 0xFF + 1)
round = 0;
else
round = M & 1;
// 构建浮点数表示并返回
return x ? (S | ((E + 0x7F) << 23) | M) + round : 0;
}