CSAPP lab1 data lab

记录,会有错的地方,

bitXor

异或的表达式是
在这里插入图片描述
题目要求是只要~和&,尝试过用德摩根率
在这里插入图片描述
但是这样太麻烦了,这里我是用同或取反得到的
在这里插入图片描述

int bitXor(int x,int y){
	return ~(~x&~y)&(~x&y);
}

tmin

要求返回int类型的最小值,int是有符号数,有符号数的最小值就是最高位为1,其余位为0,让1<<31位即可得到

int tmin(void)
{
	return 1<<31;
}

isTmax

判断x是否是Tmax,若是返回1,反之0,首先构造出Tmax,Tmax与Tmin的联系
Tmin就是最高位是1,其余是0,Tmax是最高位是0,其余是1,也就是说Tmax = ~Tmin

tips

判断是否是一个数的时候,用异或比较好

int isTmax(int x){
	//若是Tmax,异或得到的结果是0,要取反
	return !(x^(~(1<<31));
}

allOddBits

如果一个数奇数位为1返回1
第一步:把这个数构造出来,题目有限制,只能用一个字节的,但是我们可以通过移位的方式来构造
第二部:把给出的x与我们构造的数相与,再和我们构造的进行异或,就是看x是否是等于我们构造的数,注意,最后结果需要取反

int allOddBits(int x){
	int mask = 0xaa;
	mask = mask + (temp<<8) +(mask<<16)+(mask<<24);
	return !((x & mask)^mask);
}

negate

我看其他人说是求相反数,我只是写一下这个算的过程
这里以2为例子
0000 0000 0000 0000 0000 0000 0000 0010
~2
1111 1111 1111 1111 1111 1111 1111 1101 (补码,所以要再求补)
1000 0000 0000 0000 0000 0000 0000 0010(反码)
1000 0000 0000 0000 0000 0000 0000 0011(补码)
所以~2=-3,加1得到-2
来点推导
无论哪个数
x+~x=-1 -> x + ~x +1 =0 -> ~x + 1=-x

int negate(int x){
	return ~x+1;
}

isAsciiDigit

判断给定的int值是否在’0’和’9’之间,就是实现一个不等式
48<=x<=57,拆成两个式子

  1. 48<=x => x-48>=0
  2. x<=57 => 57 -x>=0
    -x = ~x+1,如果x不在这个区间里面那么必定有一个式子的值是小于0的,也就是他的符号位是1
	int a=x+(~(0x30)+1);
	int b=(~x+1)+0x39;
	int c=a>>31;
	int d=b>>31;
	return !c&!d;

conditional

实现一个x?y:z
我们想要达到的目的是构造一个mask使得当
x=0 mask & x =0 且 mask & z =z
x!=0 mask & x= x且 mask & z=0
这里解释一下代码中的!!x,!是C语言的取反运算符,得到的结果只有1和0,非0取反为1,0取反为1
当x=0的时候
!!x=0,0<<31 =0,再右移依然等于0
当x!=0的时候,!!x=1
1<<31 = Tmin ,Tmin>>31=-1,~-1=0,
为什么~-1=0
-1 在计算机中表示是
1111 11111 1111 11111 1111 11111 1111 11111(补码)
进行取反
0000 0000 0000 0000 0000 0000 0000 0000(符号位是0,那么这个就是真值了,就是0了)

int conditional(int x,int y,int z){
	x = ((!!x)<<31)>>31;
	return (~x & z) | (x & y);
	
}

isLessOrEqual

当x<=y返回1,x>y 返回0
这里分两种情况

  1. 异号,只要x是负数,y是整数返回1即可
  2. 同好,若y-x>=0就返回1
    y-x在代码中反应出来就是 y+(~x+1)
int isLessOrEqual(int x,int y){
	int signX = (x>>31) & 1;//这里改成(x>>31)^0也是可以的
	int signY = (y>>31) & 1;
	int different = signX ^ signY;//若x,y为异号就是1,反之为0
	int special = different & signX; // 这个是判断第一种情况的,就是x为负数(signX=1),y为正数(signY=0)
	// 若y>=x 那么y-x>=0 那么符号位是0,!0=1 1&1=1
	int sign = !isEqual & !((y+~x+1)>>31) & 1;
	return special | sign;
}

logicalNeg

实现运算符!
当x =0 返回1,这一步特别好搞,直接+1
当x!=0 返回0,若是按照上面的做法就是当x!=0,我们想办法让他=-1并且不能影响x=0的情况,再直接加一
这里有一个小技巧,让x与-x进行异或再右移31位,这样非0的数最后得到的结果恒等于-1,x与-x的符号位是相反的,最高位必然是1,那么再进行右移操作的时候会被移动成-1,因为这里的右移是算数右移

int logicalNeg(int x){

	int cmp = (x^(~x+1))>>31;//非0的数这一步的结果恒等于-1,若为0,那么cmp=0;
	return cmp +1;
}

howManyBits

计算一个int类型需要多少位
提示:这里可能用的了二分的思想
若是正数需要找到最高位的1
若是负数需要找到最高位的0,为什么呢?因为在展开求和的过程中只有1是有效的,0无效

我们反着来试试,我们要找24这个数需要多少位来表示,我们找最高位的0出现的位置
11000,最高位0出现的位置是第3位,但是3位表示不了
-24呢?由于是用补码存储的,原先有很多0,我的意思如下所示
1000 0000 0000 0000 0000 0000 0001 1000(看到没有符号位后面有很多0,由于补码需要由原码取反+1得到,所以原先有很多0变成了1)
1111 1111 1111 1111 1111 1111 1110 0111
1111 1111 1111 1111 1111 1111 1110 1000
这里我们统一转换成正数,若是负数转换成正数

int howManyBits(int x){
	int sign = x>>31;//取得符号位 <0则为1 >0则为0
	int res = 0;
	//为0不用管,为1 需要对x进行取反
	int tmp = (sign & ~x) | (~sign &x);
	x = tmp;//更新x的值
	//先右移15位若不为0,那么!!(tmp>>15)恒等于1,1<<4就是16;若右移15位为0,那么!!(tmp>>15)恒等于0,0<<4=0
	res = !!(tmp>>15)<<4;
	//在这种情况下 >> 等同于除法(向下取整) x / 2 ^res 
	tmp = x >> res;
	// 注意这里有个优先级的问题 式子展开:res = res | !!(tmp>>7)<<3,先算括号里面的,|的优先级大于<<
	// 在这里,按位或也就是|是等同于加法的(因为没有进位)
	// 假设tmp>>7不等于0 那么 就是 1<<3 = 8 
	// 16 | 8 
	/*
		1 0000  (16的二进制)
	  | 0 1000
	  = 1 1000 (24的二进制)
	下面就是一样的
	*/ 
	res |= !!(tmp>>7)<<3;//1
	tmp = x>>res;
	res |= !!(tmp>>3)<<2;//2
	tmp = x>>res;
	res |= !!(tmp>>1)<<1;//3
	tmp = x>>res;
	//看各个位是不是1,我们假设这个数是5哈, 那么标号为3的res是等于2的,tmp=5>>2=1
	res|=!!tmp;
	//要算符号位
	return res+1;
}

floatScale2

实现乘以2
首先来看一下IEEE 754标准(float)
在这里插入图片描述
在这里插入图片描述
思路:

  1. 取得 s(阶符),exp,frac
  2. 判定NaN,非规格化的
  3. 处理规格化的
unsigned floatScale2(unsigned uf){
	unsigned s = (uf>>31)&1;
	unsigned expr = (uf>>23)&0xff;
	unsigned frac =uf & 0x7FFFFF;
	if(expr ==0 && frac ==0)
		return 0;
	//NaN
	if(expr == 0xff)
			return uf;
	//非规格化的 denormalize
	if(expr ==0)
	{	frac<<=1;
		//前面提到了,在没有进位的情况下,按位或就可以实现加法
		return (s<<31) | frac;
	}
	//规格化的 normalize
	expr++;
	return (s<<31) | (expr<<23) | frac;
}

floatFloat2Int

将一个浮点数转为整数

  1. 取出s,expr,frac
  2. 判定0,NaN
  3. 处理正常情况

关于隐含的1
在这里插入图片描述

int floatFloat2Int(unsigned uf){
	unsigned s = (uf>>31)&1;
	unsigned expr = (uf>>23)&0xff;
	unsigned frac =uf & 0x7FFFFF;
	if(expr ===0 && frac==0)
		return 0;
	//判定为NaN
	if(expr ==0xff)
		return 1<<31;
	//判定为非规格化的
	if(expr==0)
	{
		//bias(偏置值) = (2 ^ (n-1))-1 
		//非规格化的数 127= (2^(8-1))-1 之所以是8是因为IEEE 标准中float expr占8位
		//e = 1-127=-126 都2^-126了,这个数字无限接近于0,哪怕尾数全为1,也是无限接近于0,这里直接返回0
		return 0;
	}
	//规格化的 e = expr - bias 并且 frac = 1 + frac
	int e = expr - 127;
	//先把 隐含的1加上 并且此时的frac已经是乘以2^23次方的结果,接下来就是要根据e来判断是否需要左移和右移
	// e>=23 则左移,左移 (e-23)
	// e<23 则右移,右移 (23-e)
	frac = (1<<23) | frac;
	if(e>31)//e是31位,左移32位(才能进入到这个条件分支里面)就直接爆掉了
		return 1<<31;
	else if(e<0)
		// frac 是一个1.xxxxxx的小数,并且当e<0的时候等同于 (2 ^e)*1.xxxxxx =>1.xxxxx/2^e
		// 比如说1.000001/2 这个可以直接当成0
		return 0;
	if(e>=23)
		frac<<=(e-23);
	else
		frac>>=(23-e);
	if(s)
		//若是负数,返回其绝对值
		return ~frac+1;
	return frac;
		
	
}

floatPower2

对浮点数乘以2^x

关于非规格化和规格化尾数的为什么一个可以为全0,另一个不行的原因

这个部分的内容可能会有错误,请谨慎阅读
因为非规格化浮点数的尾数部分前面没有隐含的1,所以它可以表示0和无限接近于0的数,表示0就是尾数全部为0
而规格化的尾数部分前面有一个隐含的1,只看尾数部分它最小就是1

非规格化的最值

tips:非规格化的浮点数尾数不能为0

非规格化的特点expr =0, 真实的指数=1-127=-126
当小数点最后一位是1,其余全部是0的时候达到最小值,也就是min = 2 -126 * 2-23 = 2-149
当小数点后面全为一的时候达到最大值,这里我们算上界也就是max=2-126*21=2-126
那么最值[2-149,2-126)
处理非规格化的时候我们想要表示2x的时候要变一下思路
2-126*2M=2x x是我们要表示的指数,这个M是要算出来的,他的表达式为x+126

规格化的最值

tips:规格化的浮点数可以为0

规格化的特点expr≠0且expr≠255
当expr=1(那么真实的指数部分的值=1-127=-126),尾数部分全为0的时候达到最小值,即2 -126*20=2-126
当expr=254(指数部分的值=254-127=127),位数部分全部为1的时候达到1最大值,即2127*21,注意他到不了2128
所以最值为[2-126,2128)

unsigned floatPower2(int x) {
    if(x<-149)
    //超出了表示范围
            return 0;
    else if(x<-126)
    {     //处理非规格化的
            //真实的指数值(exprReal) = expr-126,但是我们要反过来,所以需要加上126
            // 加上23的原因是因为IEEE标准规定23~30位是表示expr的,我们需要把我们表示的expr推到23位那里,所以需要加上23
            int shift =  23+(x+126);
            return 1<<shift;
    }
    else if(x<=127){
        //处理规格化的
        int expr = x+127;
        return expr<<23;
    }else
    		//太大了也可以说成超过了表示范围返回INF,
            return 0xFF<<23;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值