CSAPP datalab

1.bitXor
函数功能:使用位运算实现异或
思路:将x中为0y中为1,x中为1y中为0的位提取出来,使得位不同的位为0,最后取反。
int bitXor(int x, int y) {
return (((~x&y) & ((x&y))));
}

2.tmin
函数功能:返回int类型最小值
思路:int 类型的最小值是0x8000000,所以使用左移操作获取0x80000000即可实现
int tmin(void) {
return 1 << 31;
}

3.isTmax
函数功能:判断x是否为int类型最大值
思路:int类型的最大值为0x7fffffff,使用左移操作获取0x7fffffff,使用异或运算判断x是否与0x7fffffff相等,再使用!使得相等时返回1,不相等时返回0。
int isTmax(int x) {
return !(x ^ ~(1 << 31));
}

4.allOddBits
函数功能:判断x的所有奇数位是否都为1
思路:使用与运算将所有奇数位都提取出来,再通过异或0xaaaaaaaa判断是否所有奇数位都为1
int allOddBits(int x) {
int t = 0xAA << 24 | 0xAA << 16 | 0xAA << 8 | 0xAA;
return !((x & t)^ t);
}

5.negate
函数功能:求相反数
思路:补码中的求相反数的步骤为全部取反再加1
int negate(int x) {
return ~x + 1;
}

6.isAsciiDigit
函数功能:判断x是否为0-9的ASCII码
思路:判断x是否符合0x30 <= x <= 0x39,将这个条件分解为x-0x30>=0以及0x39-x>=0,所以将x加上0x30的相反数判断符号位是否为0,将x的相反数加上0x30判断符号位是否为0,如果两者的符号位都为0那么返回1。
int isAsciiDigit(int x) {
return !(((x + ~0x30 + 1) & (1 << 31)) | ((0x39 + ~x + 1) & (1 << 31)));
}

7.Conditional
函数功能:实现x ? y : z的效果
思路:通过x=~(!!x) + 1使得当x非0时的值为0xffffffff,等于0时的值为0,此时~(0xffffffff)=0,0xffffffff意味着任何数和它进行与操作都会不变,0x0和任何数与操作都会变为0,再利用或操作使得为0的一边忽略掉。
int conditional(int x, int y, int z) {
x = ~(!!x) + 1;
return (x & y) | (~x & z);
}

8.isLessOrEqual
函数功能:判断x<=y是否成立
思路:看到小于操作很容易想到的是使用减法实现这个函数,也就是y-x>=0(y加上x的相反数的结果符号位是否为0)这种思想来判断条件是否成立。但是在x和y异号的情况下有可能发生溢出,使得上面这种方案不可行。对于这个函数,需要先判断x和y是否异号,如果异号且x的符号位为0则x>y,如果异号且x的符号位为1则x<y。通过同号,则通过x加上y的相反数的结果中的符号位是否为x来判断,如果为1则x<=y成立。
int isLessOrEqual(int x, int y) {
int sign1 = (x >> 31) & 1;
int sign2 = (y >> 31) & 1;
int samesign = !(sign1 ^ sign2); // 是否同号
int flag1 = !(x ^ y); // 是否相等
int flag2 = !samesign & sign1; // 异号x为负数
int flag3 = samesign & (((x + 1 + ~y) >> 31) & 1); // 同号x-y<0
return flag1 | flag2 | flag3;
}

9.logicalNeg
函数功能:实现!运算
思路:对应0和非0数值,他们的一个的区别在于0的相反数为0(符号位为0),而非0值的相反数的符号位和该数值的符号位相反。
int logicalNeg(int x) {
return ~((x | (~x + 1)) >> 31) & 1;
}

10.howManyBits
函数功能:判断x使用补码需要多少位来表示
思路:对于正数,需要寻找的是最后一个为1的位,对于负数,需要寻找的是最后一个为0的位,为了将正负数都统一为只需寻找最有一个为1的位对x进行以下处理x = (~sign & x) | (sign & ~x),其中sign为x>>31。如果x为正数则,sign=0x0,如果x为负数,sign=0xffffffff。所以对于上面x的处理的意思为,如果x为正数,x保持不变,如果x为负数,对x进行按位取反。对于一个数要判断它的最后一个1在哪位,可以利用二分的思想处理。一个int类型的数32位,先对查找这个数的16-31位,如果存在则继续查找24-31位。如果这个数的16-31位不存在1,那么到8-15位去查找,以此类推。在实现中可以使用!操作快速地判断某一段中是否存在1,例如在判断16-31位时可以使用b16 = !!(x >> 16) << 4来获取下次要移位的位数,如果存在1,那么下次要右移16位,对高16位进行判断,如果不存在,那么下次右移的位数为0,判断低16位。
int howManyBits(int x) {
int sign = x >> 31;
x = (~sign & x) | (sign & ~x); // 如果x为正数保持原型,如果x为负数取反,使得非符号位最高位为1
int b16 = !!(x >> 16) << 4; // 如果大于16位,那么b16 = 16否则为0
x >>= b16;
int b8 = !!(x >> 8) << 3; // 如果大于16位那么此时取的是16-23位,否则为0-7bit
x >>= b8;
int b4 = !!(x >> 4) << 2;
x >>= b4;
int b2 = !!(x >> 2) << 1; // 0-1
x >>= b2;
int b1 = !!(x >> 1);
x >>= b1;
int b0 = x;
return b16 + b8 + b4 + b2 + b1 + b0 + 1;
}

11.floatScale2
函数功能:返回uf*2的值
思路:先获取指数域和符号位,如果指数域全部为0那么说明uf为非规格化数,此时直接整体右移保持符号位不变即可实现乘2操作。如果指数域全部为1那么,uf此时的数值为无穷大或者无穷小,直接返回原值。如果uf * 2的结果为Nan,返回对应符号的Nan。其他情况将指数域+1返回。
unsigned floatScale2(unsigned uf) {
// 对规格化数exp直接加1
int exp = (0x7f800000 & uf) >> 23;
int sign = uf & 0x80000000;

// 此时为非规格化数
if (exp == 0)
    return (uf << 1) | sign;  // 左移后保持符号位不变

// 无限大或者无限小,直接返回uf
if (exp == 255)
    return uf;
exp++;
// 若结果为nan,将小数部分置为0
if (exp == 255)
    return 0x7f800000 | sign;

return (uf & 0x807fffff) | (exp << 23);
}

12.floatFloat2Int
函数功能:将uf转换为int类型
思路:获取指数域exp、符号位sign和尾数域frac。对于规格化数需要将frac的第24位置为1,所以frac = (uf & 0x7fffff) | 0x00800000,获取指数域的十进制值exp = ((uf & 0x7f800000) >> 23) - 127。将浮点数转换为整数的过程相当于将frac中隐藏的小数点右移exp位,同时将小数点后面的数值截断。如今小数点位于23位处,如果exp大于23,那么需要将frac左移exp-23位,如果exp小于23需要右移23-exp位把小数点截掉,最后将frac转换为补码表示。
int floatFloat2Int(unsigned uf) {
int sign = uf >> 31;
int exp = ((uf & 0x7f800000) >> 23) - 127; // 获取十进制指数
int frac = (uf & 0x7fffff) | 0x00800000; // 最高位补上1

// 指数位和尾数位都为0
if (!(uf & 0x7fffffff))
    return 0;
if (exp < 0)
    return 0;
if (exp > 31)
    return 0x80000000;
// 省略小数点 1.xxx
if (exp > 23)
    frac <<= (exp - 23);
else
    frac >>= 23 - exp;

// 设置符号位
if (!((frac >> 31 ^ sign)))
    return frac;
else if (frac >> 31)
    return 0x80000000;
else // 计算补码
    return ~frac + 1;

}

13.floatPower2
函数功能:返回2.0^x,其中结果为float的形式。
思路:因为IEEE 754中的指数是阶码,所以通过exp=x+127获取返回结果的指数域。如果exp小于等于0,直接返回0。如果exp>=255,返回INF。如果计算出的结果在表示的范围内,那么直接exp右移23位返回(规格化数尾数最高位隐藏为1)。
unsigned floatPower2(int x) {
int exp = x + 127; // 获取指数位阶码
if (exp <= 0)
return 0;
if (exp >= 255)
return 0xff << 23;
return exp << 23;
}

Datalab测试结果:
在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值