因为实验环境是unix ,所以要搭环境 我用的是docker 非常方便
这边建议参考学姐的博客 总结的非常详细
https://blog.csdn.net/weixin_52259822/article/details/123610165
不过就是遇到了make: command not found的问题 当使用yum -y install gcc automake autoconf libtool make解决时候
显示 mirrorlist: No URLs in mirrorlist
后来查了发现Centos8于2021年年底停止了服务,大家再在使用yum源安装时候,出现下面错误“错误:Failed to download metadata for repo ‘AppStream’: Cannot prepare internal mirrorlist: No URLs in mirrorlist” 解决方法如下:
https://cloud.tencent.com/developer/article/1993317
如果出现bash: wget: command not found 的问题
yum -y install wget
1.bitXor
只使用~和& 实现 ^
学过离散的话,我们可以知道 a^b = (a&~b) | (~a&b)
那么此时只需要将 | 转换。这里就需要用到德摩根公式了:
总的推导:
int bitXor(int x, int y) {
return ~(~x&~y)&~(x&y);
}
2.tmin
获取对2补码的最小 int
值
审题,注意是补码的最小值,那么可知符号位为1 其余为0即可
这里用移位运算实现
int tmin(void) {
return 0x1<<31;
}
3.isTmax
查看是否是补码最大值
我们知道,补码的最大值是01111... 可以发现 补码最大值+1 == 补码最小值(补码最大值取反)但要特判下,如果值为0的话也是满足这个条件的,所以我们要把0的情况单独拎出来:
这里可以用 !!x 来判断x是否有值
int isTmax(int x) {
x = ~x;
return !(x ^ (~x + 1)) & (!!x);
}
4.allOddBits
判断是否所有奇数位为1 偶数位为0
这里想的是构造奇数位为1 偶数位为0的数,然后两者取异或对比即可
int allOddBits(int x) {
//1010 1010
int m = (0xAA << 8) | 0xAA;
m = (m << 16) | m;
return !((x & m) ^ m);
}
5.negate
对x取反,之前题目做过
int negate(int x) {
return ~x+1;
}
6.isAsciiDigit
让我们判断一个数是否位于0x30
到0x39
之间。
这道题方法是0x39 - x >=0 ,0x30 - x <=0 判断正负就行,不过刚开始没处理好第一反应是用异或
int isAsciiDigit(int x) {
int a = 0x39 + (~x + 1); // 0x39 - x >=0
int b = 0x30 + (~x + 1); // 0x30 - x <=0
int c = 0x1<<31;
int m = a&c;
int n = b&c;
// m^n
return !m&!n;
}
7.conditional
实现三目运算符 x ? y : z
只要x
不为0,我们就返回y
;否则返回z
。
根据做过的题其实可以很容易就做出来,不过这里比较麻烦的是如何构造if else的条件
这里巧妙的是我们可以用0和1的&关系做出:
int conditional(int x, int y, int z) {
int a = !!x;
int b = ~a + 1;
return (b & y) | (~b & z);
}
8.isLessOrEqual
判断 x 小于等于 y
写过前面的题都知道,可以转换成 x - y ≤ 0 但是这里要考虑符号的正负问题:当 x 和 y 异号的时候,有可能溢出。所以我们先判断符号位,同号的话就用 x - y ≤ 0 , 异号的话只有x为正时才满足题目要求。这里的话先判断x和y是否等于,然后再分符号位情况考虑。这里&1是方便格式化。
int isLessOrEqual(int x, int y) {
//取符号位
int a = x>>31&1;
int b = y>>31&1;
//判断同异号
int isab = !!(a ^ b);
// x - y
int k = x + (~y + 1) >> 31;
int m = (k >> 31)&1;
return !(x ^ y) | (m & !isab) | (isab & !!x);
}
9.logicalNeg
让我们实现!
运算符 当x等于0时候 返回1 反之返回 0
还是受到前面写的题的启发,找特殊点:只有0以及补码的最小值的相反数为其本身。至于如何区分0和补码最小值呢,我们可以巧妙地用两者的补码的符号位来判断。
int logicalNeg(int x) {
int k = ~x + 1;
return ((k | x)>>31) + 1;
}
10.howManyBits
判断一个数在二进制补码表示下,最少需要多少位才能表示出来
我们看给出的样例:12(01100) = 5 可以看出 当x为正数时,我们需要从最高位找到第一个1的位然后+1 , 当x为负数时,我们只需找到最高位数即可。因为不能用for循环,所以推荐用二分的方法去做:先看16位 ,再看8位,4位,2位。。。这里用了一种很巧妙的方法判断正负号,使得我们可以统一寻找最高位为1的位。
int howManyBits(int x) {
//二分
int a = x>>31;
//如果x为正的话 不变 为负的话 负数的最高位同样为1
x = (a & ~x) | (~a & x);
//!!(x>>16)来判断位数上是否有1
int b_16 = !!(x>>16)<<4;
//如果有 则将x右移16位
x = x>>b_16;
int b_8 = !!(x>>8)<<3;
x = x>>b_8;
int b_4 = !!(x>>4)<<2;
x = x>>b_4;
int b_2 = !!(x>>2)<<1;
x = x>>b_2;
int b_1 = !!(x>>1);
x = x>>b_1;
int b_0 = x;
//别忘了 +1 !!!
return b_16+b_8+b_4+b_2+b_1+b_0+1;
}
11.floatScale2
浮点数可以拆分成三部分:符号位s
,阶码exp
,小数frac
注:M是二进制小数!
如下图:
其中浮点数由阶码决定了是什么类型:
1.规格化:当阶码二进制不全为0且不全为1时 也就是1.0101010这样的
2.非规格化:当阶码二进制全为0时 也就是0.0101010这样的
3.特殊值:当阶码二进制全为1时,分为无穷小,无穷大(阶码全为1小数全为0),NaN
首先判断是否为0时,因为在浮点数中正0和负0是有区别的,所以我们也直接返回参数。再判断是否为无穷大或NaN 当为无穷或NaN,直接返回参数。当剩下的情况,分为非规格化和规格化判断,当为非规格化时,只有小数位,像0.0101....这样,只考虑frac即可。当为规格化时,根据IEEE公式,要expr + 1 ,然后 这样就相当于乘以了2(因为expr是2的次方)
unsigned floatScale2(unsigned uf) {
// s expr frac
unsigned s = (uf >> 31) & 0x1;
unsigned expr = (uf >> 23) & 0xFF;
unsigned frac = uf & 0x7FFFFF;
//0
if (expr == 0 && frac == 0) {
return uf;
}
//无穷 or NaN
if (expr == 0xFF) {
return uf;
}
//非规格化
if (expr == 0) {
//E = expr - 127 = -127
//frac
frac = frac << 1;
return (s << 31) | frac;
}
//规格化
//E = expr - 127
expr = expr + 1;
//根据IEEE
return (s << 31) | (expr << 23) | frac;
}
12.floatFloat2Int
单精度浮点数转换为int
这里我们还是分多种情况考虑:先判断是否为 0 。再判断是否无穷 or NaN 。紧接着如果是非规格化的话,expr = 0 , E = expr - 127 = -127 ,根据IEEE可知 ,(-1)^s * M * 2^E ,所以值太小了,在int里为0。如果是规格化,这里有个巧妙的理解方法,我们通过 E 来决定小数点的位置,当E大于等于23时(因为小数位只有23位)可以看成二进制数往右再移(E - 23)位 反之二进制数往左移动(23 - E)位 舍弃(23 - E)位的数。最后要特殊判断一下,溢出和得到的值小于1和得到的值为负数的情况。
int floatFloat2Int(unsigned uf) {
// s expr frac
unsigned s = (uf >> 31) & 0x1;
unsigned expr = (uf >> 23) & 0xFF;
unsigned frac = uf & 0x7FFFFF;
//0
if (expr == 0 && frac == 0) {
return 0;
}
//无穷 or NaN
if (expr == 0xFF) {
return 1 << 31;
}
//非规格
if (expr == 0) {
//E = expr - 127 = -127
//(-1)^s * M * 2^E
return 0;
}
//规格化
int E = expr - 127;
frac = frac | (1 << 23);
//当return大于32位的时候会爆掉(E>31) 当return小于0的时候为0(E<0)
if (E > 31) {
return 1 << 31;
}else if(E < 0){
return 0;
}
//当E >= 23 可以看成二进制数往右再移(E - 23)位 反之二进制数往左移动(23 - E)位 舍弃(23 - E)位的数
if (E >= 23) {
frac = frac << (E - 23);
}else{
frac = frac >> (23 - E);
}
//负数
if (s){
return ~frac + 1;
}
return frac;
}
13.floatPower2
用单精度浮点数表示2的x次方
这道题要注意x的范围,当数为非规格化时,什么时候x最小呢?我们可以从(-1)^s * M * 2^E考虑,其实这里我们可以舍去s,因为总是为正数,考虑M * 2^E ,M的最小值是2 ^ -23 ,E最小值为 -126 , 所以x最小值为 -23 - 126 = -149 ,而M * 2 ^ E 最大值为 二进制所有位数为 1 ,因为是相乘的,所以最大值为 -126。当数为规格化时,-126次方(E = exp - bias)是x的最小值。
unsigned floatPower2(int x) {
if (x < -149) {
//值小返回0
return 0;
}else if (x > 127){
//值大返回无穷
return (0xFF) << 23;
}else if (x < -126){
//E = -126
return 1 << (23 + 126 + x);
}else{
return (x + 127) << 23;
}
}
做完之后对位运算有了更深刻的理解,收获非常大。