CSAPP实验----datalab(详解)(附每一步的讲解)

这篇具有很好参考价值的文章主要介绍了CSAPP实验----datalab(详解)(附每一步的讲解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.bitXor   

例:
x=4 = 0100(二进制)
y=5 = 0101(二进制)
x^y= 1=0001(二进制)
对于两个二进制数字i和j,若i和j在某一位上相同(都为0或都为1),则它们的按位异或结果该位为0; 如若不同,即i和j在该位上一个为0,另一个为1,则它们的按位异或结果该位为1
我们可以利用上述特性来实现按位异或的操作
 总的来讲我们要关心哪些位不相同,将都不相同的位置操作为1相同的位置改为不相同,这样&之后得到的结果就是a和b的按位异或。

int xor(int a, int b) {
    return ~(~a & ~b) & ~(a & b);
}

2.Tmin

返回int类型的最小值,即-2^31。

  1. << 运算符是按位左移运算符,将表达式左边的数值向左移动右边的指定数量的二进制位。
  2. 1<<31 表示将整数1的二进制位向左移动31位(移出的部分被丢弃),相当于将右侧的0填充到左侧的空缺位置,这样得到的数值就是int类型最小值的绝对值的二进制补码形式。
  3. 在二进制补码的表示方式中,最高位表示符号位,为1表示负数,为0表示正数。因此,1<<31 表示的值就是 int 类型最小值的绝对值(也就是 2^31)的二进制补码,最高位为1,表示其为负值。
  4. 返回结果时,该函数直接返回1<<31,也就是-2^31,即int类型的最小值。
    
    int tmin(void) {
    
    return 1<<31;

因此,该函数的作用是返回int类型的最小值。

3. isTmax

1.~x: 取x的按位取反值,即将x的所有二进制位都取反(0变成1,1变成0),包括符号位。
2.x = ~x: 将按位取反后的值赋给x,原本的x值就被覆盖了。
3.(~x + 1): 将~x的值加1,相当于对~x进行取反加1的操作,由于int类型是二进制补码形式存储,所以最高位是符号位1,~x + 1的值就是int类型的最大值。
4.x ^ (~x + 1): 对x和(~x + 1)进行按位异或操作,得到的结果是如果x为int类型的最大值,则结果为0,否则结果不为0。
5.!(x ^ (~x + 1)): 对上面得到的结果取反,对于x为int类型的最大值来说,结果为1,否则为0。
6.!!x: 对x进行两次逻辑非(!)操作,得到的结果是x是否为非0值的bool值,即如果x为0,则结果为0,否则为1。
7.!(x ^ (~x + 1)) & (!!x): 将第5步和第6步的结果进行按位与(&)操作,得到的结果是如果x为int类型的最大值并且不为0,则结果为1,否则为0。

因此,整个函数的功能就是判断给定的整数x是否为int类型的最大值。


int isTmax(int x) {
x = ~x;
return ! (x ^ (~x + 1)) & (!! x);
}

4. alloldbits

这段代码的作用是判断给定的整数x二进制表示中,所有偶数位(从右往左数,从0开始计数)是否都为0。如果偶数位全部为0,则返回1,否则返回0。
解析如下:

1.int mask=0xAAAAAAAA; 定义了一个int类型的常量mask,其二进制表示中所有的奇数位(从右往左数,从0开始计数)都被设置为1,偶数位都被设置为0,如下图:
1010 1010 1010 1010 1010 1010 1010 1010
2.将mask与x执行按位与(&amp;)运算。
3.执行按位与(&)运算后,结果的偶数位部分将全部为0,奇数位部分同样也全部为0。
如果x的所有偶数位都为0,那么按位与后得到的结果也应该全部为0,此时函数会返回1;否则返回0。
因此,该函数的作用是判断给定的整数x二进制表示中,所有偶数位(从右往左数,从0开始计数)是否都为0,并将结果返回。

int allOddBits(int x) {
int mask=0xAAAAAAAA;
  return !(x&mask);
}

5. negate

在二进制中表示负数时,会把位权的最高位上的数字作为符号来使用,因此,位权的最高位也称为符号位。符号位为 0,表示正数;符号位为 1,表示负数。
这里我们用到的方法叫做“二进制的补数”,补数就是用正数来表示负数。求补数的方法叫做“取反加一”。
代码如下:

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

6.isAsciiDigit

1.将x加上(~0x2f),得到的结果是(x-0x30)-1的16进制补码表示,其高八位都是1。然后判断这个结果是否小于等于0xFF,即判断(x-0x30)-1是否小于等于0xFF,这等价于判断x-0x30是否小于等于0x9,也就是判断x是否小于等于0x39。如果是,upper的值为1,否则为0。
2.将0x3a加上(~x),得到的结果是0x3a-x-1的16进制补码表示,其高八位都是1。然后判断这个结果是否小于等于0xFF,即判断0x3a-x-1是否小于等于0xFF,也就是判断x是否大于等于0x30。如果是,lower的值为1,否则为0。
3.将upper和lower进行按位与运算,如果上述两个条件都满足,则返回1,否则返回0。

需要注意的是,该函数假设char类型为有符号类型,在有符号类型的算术右移时会进行符号扩展,因此能正确地处理负数的情况。如果char类型为无符号类型,则需要将右移运算转换为逻辑右移运算,例如将(x + ~0x2f) >>8替换为((unsigned)x + ~0x2f) >>8即可。

int isAsciiDigit(int x) {
  int upper = !((x + ~0x2f) >> 8);  
  int lower = !((0x3a + ~x) >> 8);  
  return upper & lower;             
}

7.conditional

1.将x与逻辑非(!)运算符结合,得到x的逻辑非值。如果x为0,其逻辑非值为1;否则,其逻辑非值为0。
2.将逻辑非值用取反(~)运算符进行取反,得到一个全1或全0的掩码。其中,如果x不为0,掩码为0xFFFFFFFF;否则,掩码为0x00000000。这里使用按位取反后加1(~z+1)的方法,将全1掩码变成了0xFFFFFFFF,将全0掩码变成了0x00000000。
3.将掩码与y进行按位与(&amp;)运算,得到的结果为y的值(如果x不为0),或者0(如果x为0)。将掩码取反后再与z进行按位与(&amp;)运算,得到的结果为z的值(如果x为0),或者0(如果x不为0)。
4.将上述两个结果进行按位或运算(|)得到最终结果,即如果x不为0,则返回y,否则返回z。

int conditional(int x, int y, int z) {
  int mask = ~(!x) + 1;        
  return (mask & y) | (~mask & z); 
}

8.isLessOrEqual

1.计算x的符号值signx,如果x是非负数,则signx等于0;否则,signx等于-1。同理,计算y的符号值signy。
2.计算y-x的符号值diffsign,如果y-xsign等于1;否则,diffsign等于0。这里使用的是(y+~x+1)的方法来计算差值,相当于计算y-x的16进制补码表示。如果y-x超出了int类型的范围,则diffsign的值就不再是1或0,无法判断大小关系。因此,需要单独讨论符号相反时的情况。
3.判断x和y的符号是否相同。如果相同,则可以根据差值的符号来判断大小。如果不同,则必有y大于x(如果y为非负数),或者y小于x(如果y为负数)。
4.如果x和y的符号不同,且x为负数,则opposign等于-1,否则opposign等于0。
5.根据符号位和差值的符号来判断x是否小于等于y,即如果x和y符号不同,则必有y大于x(如果y为非负数),或者y小于x(如果y为负数);如果x和y符号相同,则可以根据diff_sign的值来判断大小。

需要注意的是,由于加减有可能会溢出,因此需要将符号相反的情况单独处理。

int islessOrEqual(int x, int y) {
  int sign_x = x >> 31;  // 得到x的符号位代表的数值,0代表正数,-1代表负数
  int sign_y = y >> 31;  // 得到y的符号位代表的数值,0代表正数,-1代表负数
  int diff_sign = ((y + ~x + 1) >> 31) & 1;  // 如果y-x<0,则diff_sign为1,否则为0
  int same_sign = !(sign_x^sign_y);  // 如果x和y符号相同,则same_sign为1,否则为0
  int oppo_sign = sign_x & !same_sign; // 如果x和y符号不同且x为负数,则oppo_sign为-1,否则为0
  return ((!same_sign) & (sign_y & !oppo_sign)) | (same_sign & !diff_sign);  // 如果y>=x,则返回1;否则,返回0
}

9.logicalNeg

1.计算x的符号位sign,通过右移操作(x>>31)来提取最高位。如果x为正数,则sign为0;如果x为负数,则sign为1。
2.当x等于-1时,返回的结果应该是0,因为在2's complement中,-1的二进制表示中所有位均为1,即0xFFFFFFFF。因此,在x等于-1时,需要设置一个特殊掩码mask,该掩码的值为0xFFFFFFFF。否则,mask的值为0x00000000。
3.将sign与mask进行按位与运算,得到如果x等于-1,则将符号位取反(由0变为1);否则,保持符号不变。
4.将取反后的符号位与掩码(~mask)进行按位与运算,得到如果x不等于0的逻辑非值(即取反后的符号位的值),或者0。这一步将处理掉除了-1以外的所有情况。
5.将上述结果用按位与运算符(&amp;)运算,得到0或1的结果。

int logicalNeg(int x) {
   int sign = ((x >> 31) & 1);  // sign取值为0或1,表示x的符号位
   int mask = ~0 + !(x ^ -1);  // 如果x等于-1,则mask等于0xFFFFFFFF;否则,mask等于0x00000000
   return ((mask & sign) | (~mask & ~sign)) & 1;  // 返回x是否为0的逻辑非值
}

10.howManyBits

该函数的功能是计算用二进制补码表示整数x所需的最少位数。需要注意的是,在使用二进制补码表示时,正数和负数所需的位数可能不同。

1.计算x的符号位mask。如果x为正数或0,则mask值等于0;如果x为负数,则mask值等于-1。
对于负数x,为了能正确地计算它所需的位数,需要将x取反后再进行计算。因此,需要把x取反,即使用(mask &amp; (~x)) | ((~mask) &amp; x)的方法获取x的相反数。
2.接下来,需要统计需要对x进行多少位二进制补码表示。为了最小化所需的位数,采用了分治的方式。该算法采用了类似于二分的思想,每次砍掉一半不需要的位,从而减少所需比特位数的计算。具体来说,首先判断x的高16位是否有值。如果有值,则至少需要16位才能表示x;否则,需要用低位存储x。即通过(!(!(x &gt;&gt; 16)) &lt;&lt; 4)的方法得到这一计算所需的位数,注意只有0或1两种情况。
3.接下来,使用x = x &gt;&gt; bit_num将剩余需要表示的比特位数保存在x中。
4.接着,在x中使用类似的方法计算剩余比特位数,直到判断完成8位、4位、2位和1位。计算剩余位数所需的比特位数时,要使用(!(!(x &gt;&gt; k)) &lt;&lt; m)的方式。其中k为本次位数比较的起始位,m为需要加上的比特位数。在每次计算的结束处,将剩余需要表示的位数保存在x中,以便下一次比较时使用。
6.在计算完所有的位数后,将所有的位数相加,得到需要表示

int howManyBits(int x) {
   int mask = x >> 31;  // 如果x为正数或0,则mask为0;如果x为负数,则mask为-1
   int bit_num = 0;  // 记录需要表示x所需的比特位数
   x = (mask & (~x)) | ((~mask) & x);  // 如果x为正数或者0,则不变;如果x为负数,则取反
   bit_num = !(!(x >> 16)) << 4;  // 如果x的高16位有值,则需要至少16位存储;否则,还需低位存储
   x = x >> bit_num;  // 取得剩余的需要表示的比特位数
   bit_num = bit_num + (!(!(x >> 8)) << 3);  // 通过判断剩余位数是否需要高8位存储来更新bit_num
   x = x >> (bit_num & 7);  // 取得剩余的需要表示的比特位数
   bit_num = bit_num + (!(!(x >> 4)) << 2);  // 通过判断剩余位数是否需要高4位存储来更新bit_num
   x = x >> (bit_num & 3);  // 取得剩余的需要表示的比特位数
   bit_num = bit_num + (!(!(x >> 2)) << 1);  // 通过判断剩余位数是否需要高2位存储来

11.floatScale2

该函数的实现可以分为以下几个步骤:

从uf中获取符号位、指数位和尾数位。
判断指数位是否为0xFF,如果是,则表示uf为NaN或Infinity,此时返回uf。
如果指数位为0,即uf为denorm,则将尾数位左移一位表示2f。
如果指数位为正数,则是normal数,将指数位加1表示2f。
将符号位、指数位和尾数位按位或运算,获得2f的比特位表示。
返回得到的结果。

需要注意的是,在计算2f时,需要检查uf是否为NaN或Infinity等特殊情况,并选择适当的运算。同时,也需要考虑denorm数的情况。本实现中,使用的是位运算符,以便在不使用显式转换的情况下实现将浮点数的比特位表示按照指定方式进行调整的操作。

unsigned float_twice(unsigned uf) {
    int sign = uf >> 31;   // 获取符号位
    int exp = (uf >> 23) & 0xFF;   // 获取指数位
    int frac = uf & 0x7FFFFF;   // 获取尾数位
    if(exp == 0xFF) {   // 如果是 NaN or Infinity
        return uf;
    } 
    if(exp == 0) {   // 如果是 denorm, 将尾数位左移一位
        frac <<= 1;
    } else {   // 如果是 normal, 将指数位加1
        exp++;
    }
    return (sign << 31) | (exp << 23) | frac;   // 将三个位按位或运算得到结果
}

12.floatFloat2Int

1.从uf中获取符号位、指数位和尾数位。
2.计算指数exp,即减去偏置常数127,得到实际的指数位值。
3.如果exp小于0,表示f的值小于1,不能转换为整型,此时返回0。
4.如果exp大于等于31,表示f太大了,转换为整型会越界,此时返回0x80000000u。
5.如果exp在0到30之间,则可以将uf转换为整型。考虑符号位,如果sign为1,表示f是负数,将在下一步进行处理。
6.根据exp对尾数frac进行左移或右移操作。如果exp大于23,则将frac左移(exp-23)位,如果exp小于等于23,则将frac右移(23-exp)位。
7.如果sign为1,即f为负数,需要将frac取反,然后加上1,得到其补码形式。
8.返回转换后的整型值。

需要注意的是,在将uf转换为整型时,需要检查f的值是否在转换的范围内。由于uf可能表示NaN或Infinity等特殊值,在转换时需要做出特殊处理。

int floatFloat2Int(unsigned uf) {
    int sign = uf >> 31;   // 获取符号位
    int exp = ((uf >> 23) & 0xFF) - 127;   // 获取指数位,并减去偏置常数
    int frac = (uf & 0x7FFFFF) | 0x800000;   // 获取尾数位,并将整数位添加为1
    if(exp < 0) {   // 如果f的小于1,不可能转换为整型
        return 0;
    } else if(exp >= 31) {   // 如果f太大,超出整型范围,则返回0x80000000u
        return 0x80000000u;
    } else {
        if(exp > 23) {   // 转换的值大于1,通过左移尾数位实现
            frac <<= (exp - 23);
        } else {   // 转换的值小于1,通过右移尾数位实现
            frac >>= (23 - exp);
        }
        if(sign == 1) {   // 如果f是负数,转换后也为负数
            frac = ~frac + 1;   // 取反并加上1,得到补码
        }
        return frac;   // 返回转换后的整型值
    }
}

13. floatPower2

1.计算指数exp,即加上偏置常数127,得到单精度浮点数f的指数位值。
2.如果exp小于等于0,则表示f的值太小了,不能表示为单精度浮点数。此时,函数将返回0。
3.如果exp大于等于255,则表示f的值太大了,不能表示为单精度浮点数。此时,函数将返回+INF。
4.如果exp在1到254之间,则f可以表示为单精度浮点数,函数将返回其bit-level等价值。
5.返回转换后的单精度浮点数的比特位表示,其中指数部分通过左移23位得到,其他比特位的值为0。

需要注意的是,为了正确地表示单精度浮点数f的值,需要检查指数的值是否位于区间[1, 254]之间,才能将f的比特位表示计算得到。计算指数部分的值时,需要将x加上一个偏置常数。计算结果要进行位运算和移位文章来源地址https://www.toymoban.com/news/detail-847711.html

unsigned floatPower2(int x) {
    int exp = x + 127;   // 获取指数位,加上偏置常数
    if(exp <= 0) {   // 如果指数太小,则f表示的值为denorm或零,返回0
        return 0;
    } else if(exp >= 255) {   // 如果指数等于或大于255,则f表示的值为无穷大,返回+INF
        return 0x7F800000u;
    }
    return exp << 23;   // 得到正确的比特位表示,指数对应移位即可
}

到了这里,关于CSAPP实验----datalab(详解)(附每一步的讲解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Git 从入门到精通】2023最新版的Git安装与卸载每一步附详细讲解

    首先去官网下载Git安装包,可以直接在百度搜索Git,以下几个网站都可以。也可以点击直达,官网上下载如果不科学上网的话还是很慢的,所以我准备了一份放在了百度网盘内,需要的可以去评论区拿。 当从上面网站或者百度网盘中下载完exe文件之后,咱们就可以开始安装了

    2024年02月16日
    浏览(44)
  • c语言冒泡排序详解(分析每一步,附代码)

            冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。         它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要

    2024年02月16日
    浏览(29)
  • VMware 安装安卓虚拟机 一步一步的手把手教学

    平台:PC CPU:R7 3700X 显卡:3060 软件:VMware16 系统:Win10 1909 镜像:android-x86_64-9.0-r2-k49.iso 网址:https://osdn.net/projects/android-x86/releases/71931 我用的迅雷下载,很快 度盘链接: pwd 2022 链接:https://pan.baidu.com/s/1HaSEB_CkyE_UmJwHlvzbrw?pwd=2022 包含Android7-9三个镜像 到这里虚拟机就算创建完

    2024年02月03日
    浏览(34)
  • csapp实验5-cachelab实验详解

    1 简要分析 实验目的:熟悉cache,学会根据cache优化程序执行速度 工作空间:csim.c和trans.c 实验内容: part A:在csim.c下面编写一个缓存模拟器来模拟cache的行为,并且规定该模拟器用LRU替换策略,即替换某组中最后一次访问时间最久远的一块,还要支持一些输入可选参数 操作

    2024年02月04日
    浏览(25)
  • 解决Git Large File Storage (LFS)问题:一步一步的教程

    在这篇博客中,我们将探讨如何处理Git Large File Storage (LFS)的一些常见问题。Git LFS是一种用来处理大型文件的Git扩展,它可以让你更轻松地管理大型二进制文件,如图像、音频和视频文件,存储GIS中的.tif, .csv等数据文件有重要作用。 在使用Git和GitHub进行版本控制时,我们可能

    2024年02月04日
    浏览(26)
  • 史上最详细----阿里云创建ECS实例教程(每一步图文结合)

    进入阿里云官网,登录账号 进入控制台页面 打开侧边导航栏,进入云服务器ECS页面 点击创建实例 进入到这个页面(我这里为了方便演示,用的是旧版的页面) 选择付费模式和可用区 选择配置 选择系统镜像和存储服务 完成之后点击下一步 ps:阿里云按需付费购买实例,余额

    2024年02月11日
    浏览(29)
  • UniApp 中的路由守卫与拦截器:守护应用的每一步

    正文: 路由守卫和拦截器在前端开发中扮演着重要的角色,它们可以用来控制页面访问权限、全局请求拦截等。在 UniApp 中,路由守卫和拦截器同样具有强大的功能,能够保护应用的安全和稳定性。本文将深入探讨 UniApp 中的路由守卫和拦截器,带你领略它们的魔法与神奇。

    2024年04月25日
    浏览(28)
  • 【计算机基础】Git从安装到使用,详细每一步!扩展Github\Gitlab

    📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍 收藏 ⭐不迷路🙉 📢:内容若有错误,敬请留言 📝指正!原创文,转载请注明出处 📝 诞生 :2005年

    2024年02月09日
    浏览(37)
  • Centos7下的DNS服务器部署(每一步图文结合超详细,适用于初学者)

    关于DNS服务,网上都有很多很详细很专业的讲解,但是对于大部分初学者可能看的比较懵懂,用白话来说就是起初人们因为对大量用于访问服务器的IP地址难以记住,所以就逐渐出现了域名的形式(诸如:www.baidu.com 之类的),但是计算机本身只能识别出像192.168.10.112之类的

    2024年02月07日
    浏览(55)
  • CSAPP--ATTACKLAB实验

    目录 一、实验步骤         二、目标程序 三、实验内容第一部分:代码注入攻击 3.1.        第一关 3.2.        第二关 3.3.        第三关 四.   实验内容第二部分:面向返回的编程 4.1.        第四关 4.2.        第五关 附录A HEX2RAW的使用 附录B 生成字

    2024年02月09日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包