• scau CSAPP 第二章家庭作业


    注意!这些作业以及实验都要求在32位机器上实现哦! 

    2.59

    编写一个C表达式,生成一个字,由x的最低有效字节和y中剩下的字节组成。

    对于运算数x=0x89ABCDEF,y=0x76543210,就得到0x765432EF

    Tips. 一个字长等于32位,一个字节是8位。所以最低有效字节是末8位。

    (1)首先要取出x的最低有效字节,可以采用 x & 0xFF 的方法

    (2)再取出y的剩下字节,可采用 y & ~0xFF 的方法,

      步骤(1)(2)这里都运用到了与1相与是本身,与0相与是0的思想。

    (3)然后考虑到一个数与0相或是它本身,所以把这两部分相或就能得到结果。

    2.61

    写一个C表达式,在下列描述的条件下产生1,而在其他情况下得到0。假设x是int类型。

    A:x的任何位都等于1
    B:x的任何位都等于0
    C:x的最低有效字节中的位都等于1
    D:x的最高有效字节中的位都等于0
    限制:不能使用(==)和(!=)

    在课本第二章P39的2.1.8中,我们知道逻辑运算符(||    &&   !),他们将0视作逻辑“FALSE”(假);将所有非0值视作逻辑“TRUE”(真);计算结果总是0/1。 

     A.当x的任何位都等于1时产生1——可以先全部按位取反,这样得到任何位都应该是0,再通过逻辑非运算(!)就可以产生1

    例:0x 1111 1111取反->0x 0000 0000->逻辑非运算->0x 0000 0001,此时为非零值,返回1

           0x 1111 0000取反->0x 0000 1111->逻辑非运算->0x 0000 0000,此时为0,返回0

    emm,我觉得也可以这么记逻辑非——原来不是0(不管你是1,2,3……还是100000……),逻辑非后就都成了0;原来是0,逻辑非后就成了非0值(也就是1)

    B.当x的任何位都等于0时产生1——直接使用逻辑非运算(!)即可

    例:0x 0000 0000->逻辑非->0x 0000 0001,此时为非零值,返回1

           0x 1111 0000->逻辑非->0x 0000 0000,此时为0,返回0

    C.x的最低有效字节中的位都等于1时产生1——

    思路:2.59+2.61(A)

    (1)获取x的最低字节:x & 0xFF

    (2)套用A中的公式:!~ (x & 0xFF)

    例:0x 0000 0000 & 0xFF-> 0x 0000 0000->取反得0x 1111 1111-> 逻辑非运算得 0

    D.x的最高有效字节中的位都等于0时产生1——

    思路:(1)抽取最高有效字节->  x & (0xff00 0000)

               (2)直接返回即可

    2.64

    /*Return 1 when any odd bit of x equals 1;0 otherwise. Assume w=32 */

    /*当x的奇数位为1时返回1,否则返回0*/

    int any_odd_one(unsigned x);

    函数应该遵循位级整数编码规则,不过你可以假设数据类型int有w=32位。

     (1)首先构造掩码,使用移位运算符构造出奇数位全1的数(mask) ,

     (2)然后获取输入值 x 的奇数位,其他位清零,也就是(mask & x),然后与mask进行异或操作,若(mask & x)与mask相同则最终结果为0,然后返回其值的逻辑非,也就是返回1

    1. #include
    2. /* Return 1 when odd bit of x equals 1; 0 otherwise
    3. Assume w=32 */
    4. int any_odd_one(unsigned x) {
    5. /* Mask 0xAAAAAAAA */
    6. unsigned mask = (0xAA << 24) | (0xAA << 16) | (0xAA << 8) | (0xAA);
    7. return !((mask & x) ^ mask);
    8. void main(){
    9. printf("result %d",any_odd_one(0x10));
    10. printf("\n");
    11. }

    2.66

    /* Generate mask indicating leftmost 1 in x. Assume w =32 
    For example,0xFF00 -> 0x8000,and 0x6600 --> 0x4000.
    If x = 0.then return 0. */
    int leftmost_one(unsigned x);

    这道题理解题目我想了很久诶TUT

    先来分析一下题目给的示例——

    0x FF00的二进制表示为1111 1111 0000 0000,0x 8000 的二进制表示为 1000 0000 0000 0000

    0x 6600的二进制表示为0110 0110 0000 0000,0x 4000的二进制表示为  0100 0000 0000 0000

    再结合 Generate mask indicating leftmost 1 in x这句话,我们可以知道这道题的意思是:让最左边的1的右边全部为0.

    (1)先让最左边的1的右边全部变成1,得到新的数x

    (2)得到的x右移一位再加一,就可以得到原来的数的最左边的1的右边全为0的数了

    补充:代码中(x>>1)并没有直接+1,而是+(x&&1),这里用(x&&1)来判断x是不是0,在上面那道题也说了,逻辑运算符(||    &&   !),他们将0视作逻辑“FALSE”(假);将所有非0值视作逻辑“TRUE”(真);计算结果总是0/1。 那么在(x&&1)这个式子中,当x==0时,运算结果为0;当x是其他数时,运算结果为1

    1. #include <stdio.h>
    2. #include <assert.h>
    3. int leftmost_one(unsigned x) {
    4. x |= x >> 16;
    5. x |= x >> 8;
    6. x |= x >> 4;
    7. x |= x >> 2;
    8. x |= x >> 1;
    9. return (x >> 1) + ( x && 1);
    10. }
    11. int main() {
    12. assert(leftmost_one(0xff00)==0x8000);
    13. assert(leftmost_one(0x6600)==0x4000);
    14. return 0;
    15. }

    2.72

    You are given the task of writing a function that will copy an integer val into a buffer buf, but it should do so only if enough space is available in the buffer.

    Here is the code you write:

    /* Copy integer into buffer if space is available */

    /* WARNING: The following code is buggy */

    1. void copy_int(int val, void *buf, int maxbytes) {
    2. if(maxbytes-sizeof(val) >= 0) {
    3. memcpy(buf, (void*)&val, sizeof(val));
    4. }
    5. }

    测试这段代码后,哪怕maxbytes很小的时候,它也能把值复制到缓冲区。

    A.解释为什么代码中的条件测试总是成功。提示:sizeof运算符返回类型为size-t的值

    B.你该如何重写这个条件测试,使之工作正确。

    A.因为运算结果是无符号数,而无符号数肯定是大于等于零的,所以这个测试总是成功。

    B.条件测试改为 maxbytes >=sizeof(val)

    1. #include <stdio.h>
    2. #include <assert.h>
    3. int conditional_test(int val, int maxbytes) {
    4. return maxbytes >= (int)sizeof(val);
    5. }
    6. int main() {
    7. assert(conditional_test(0, 4));
    8. assert(!conditional_test(0, 2));
    9. assert(!conditional_test(0, 0));
    10. return 0;
    11. }

    2.77

    将整数变量x乘以不同的常数因子K。为了提高效率,我们想只用+、=、<<运算。

    A.K=17

    B.K=-7

    C.K=60

    D.K=-112

    可以通过(x<

    1. #include
    2. #include
    3. int A(int x) {
    4. return x + (x << 4);
    5. }
    6. int B(int x) {
    7. return x - (x << 3);
    8. }
    9. int C(int x) {
    10. return (x << 6) - (x << 2);
    11. }
    12. int D(int x) {
    13. return (x << 4) - (x << 7);
    14. }
    15. int main() {
    16. assert(A(19) == 17 * 19);
    17. assert(B(19) == -7 * 19);
    18. assert(C(19) == 60 * 19);
    19. assert(D(19) == -112 * 19);
    20. return 0;
    21. }

    2.85

    给定具有 k 位指数n 位小数的浮点格式,对于下列数,写出阶码(指数) E、尾数(有效数) M、小数 f 和值 V 。此外,描述位表示形式。

    A.数7.0

    B.能够被准确描述的最大奇整数

    C.最小的规格化数的倒数

    首先,我们得知道基本的知识点,我列出几个式子——

    偏置值——bias=2^(k-1)-1,其中k为阶码段的长度。例如:bias(float)=2^(7-1)-1=127

    值V——V=(-1)^S*M*2^E

    阶码的值——(1)规格化数:E=e-bias。其中e为阶码段的值。以float为例,阶码段长度为8,而规格化数的阶码段不全为0/不全为1;那么e的最小值为0000 0001,也就是1,e的最大值为1111 1110,也就是254

                          (2)非规格化数:E=1-bias

    尾数——(1)规格化数:M=1+f

                   (2)非规格化数:M=f

    计算步骤:

    A.数7.0

    (1)题目给出浮点格式为k为指数,所以bias=2^(k-1)-1

    (2)7.0的二进制表示为0b111.0,然后移动小数点直到小数点左边只有一个1,得M=0b1.110,小数部分f=0b0.11,进而得E=2,因为阶码E也叫指数,左移两位,也就是指数为2。值V就是他自己,V=7.0

    (3)另外,e=E+bias=2+2^(k-1)-1=2^(k-1)+1,所以位模式表示为0 10...01 110...

    B.能够被准确描述的最大奇整数

    (1)最大奇整数——首先,该数的二进制表示应为1111 1111...,即V=0b 1111 1111...;其次,最大:它得是个正数,因此符号位s=0;接着,能够被准确描述:所以小数点后的位数一共有确定的能够准确存储的n位,所以M=1.1111...,f=0.1111...(小数点后有n个1),E=n

    (2)同样,bias=2^(k-1)-1,e=E+bias=2^(k-1)-1+n

    (3)位模式表示为0  2^(k-1)-1+n  1111...

    C.最小的规格化数的倒数

    (1)emm,最小的规格化数,应该是个负数吧,那么符号位s=1

    (2)既然最小,e=1,M=1.00...,f=0.00...,E=e-bias=1-bias=1-2^(k-1)-1=-2^(k-1),V=(-1)^1*1*2^[-2^(k-1)]=-1/{2^[2^(k-1)]},当然这个式子看起来很“烦”,可以写成V=-2^(1-bias)

    (3)它的倒数V=-2^(bias-1),同样M=1.00...,f=0.00...,也可得E=bias-1,而e=E+bias=-1

    (4)位模式表示为 1  11...101  000...

    2.88

    请考虑以下两个基于 IEEE 浮点格式的 9 位浮点表示形式。

    格式 A

    • 有一个符号位。

    • 有 k=5 个阶码位。指数偏置量为 15。

    • 有 n=3 个小数位。

    格式 B

    • 有一个符号位。

    • 有 k=4 个阶码位。指数偏置量为 7。

    • 有 n=4 个小数位。

    下面,在格式化为 A 时为您提供了一些位模式,您的任务是将它们转换为格式 B 中的最接近的值。如果需要舍入,则应向 +∞ 舍入。此外,提供格式 A 和格式 B 位模式给出的数字值。以整数或分数形式给出这些值。

     

    本题也会用到2.85的公式~ 

    (1)格式A位:0 10110 101

    根据位模式,我们可知,符号位s-0,阶码位有5位(即k=5),bias=2^(5-1)-1=15,e=2^1+2^2+2^4=2+4+16=22,E=e-bias=7,小数位f=(2^-1+2^-3)=5/8,M=1+f=13/8

    可得V=(-1)^0*(13/8)*2^7=208

             格式B位:我是这么想的,就是求格式化A位的值的反过来。

    已知格式B值等于208,是一个整数,符号位s=0,E=7,M=13/8,f=5/8,但是此时bias=7,可得e=14,而14=2+4+8,转换为二进制就是0 1110 1010

    (2)格式A位:1 00111 110

    同理,s-1,bias=15,e=2^0+2^1+2^2=7,E=e-bias=-8,小数位f=2^(-1)+2^(-2)=6/8,M=1+f=14/8

    可得V=(-1)^1*(14/8)*2^(-8)=-7/1024

              格式B位:已知-7/1024,符号位s=1,bias=7,而A中E=e-bias=-8,e又不为负,B的指数E和A最接近的话只能是-6,这样一来e=0,可知格式B位是一个非规格化数。指数E少了-2,那只能在小数位补,由A的V式子可知B的M应等于14/32,则f=M=7/16,转为二进制为1 0000 0111

    (3)格式A位:0 00000 101

    注意!这里的阶码段全为0!所以它是一个非规格化数!

    s=0,bias=15,e=0,E=1-bias=-14,小数位f=5/8,M=f=5/8

    可得V=(-1)^0*(5/8)*2^(-14)=5/2^(-17)   这道题和答案有出入,个人认为是答案错了

           格式B位:已知5/2^(-17) ,符号位s=0,bias=7,而A中E=-14,B的指数E和A最接近的话只能是-6,即B又是个非规格化数。指数少了-8,在小数位补8,B的M应为5/2^11,但是M最多就到2^-4,我也搞不明白了……

    (4)后面的题都类似,自己算一下就好了

    答案: 

  • 相关阅读:
    JS原型链
    修改ctags让fzf.vim插件显示C,C++方法声明的标签
    什么是 ping (ICMP) 洪水 DDOS 攻击?
    第八章、定制new和delete
    机械制造企业如何借助ERP系统,做好生产排产管理?
    Hystrix 服务熔断
    Java:Java中的ThreadLocal简介
    C++中“AVL”树的模拟实现以及实现中遇见的问题
    JAVA编程思想N刷
    天月德统计
  • 原文地址:https://blog.csdn.net/zqihm/article/details/127344747