深入理解计算机系统(第三版) 家庭作业 参考答案 CSAPP HOMEWORK SOLUTIONS(持续更新中)

我自己写的,代码均经过简单测试,如果有什么不对的地方,敬请留下评论


第二章

2.55 2.56 2.57

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
    size_t i;
    for (i = 0; i < len; i++)
        printf(" %.2x", start[i]);    //line:data:show_bytes_printf
    printf("\n");
}

void show_int(int x) {
    show_bytes((byte_pointer)&x, sizeof(int)); //line:data:show_bytes_amp1
}

void show_float(float x) {
    show_bytes((byte_pointer)&x, sizeof(float)); //line:data:show_bytes_amp2
}

void show_pointer(void *x) {
    show_bytes((byte_pointer)&x, sizeof(void *)); //line:data:show_bytes_amp3
}

void show_short(short x) {
    show_bytes((byte_pointer)&x, sizeof(short));
}

void show_long(long x) {
    show_bytes((byte_pointer)&x, sizeof(long));
}

void show_double(double x) {
    show_bytes((byte_pointer)&x, sizeof(double));
}

void test_show_bytes(int val) {
    int ival = val;
    float fval = (float)ival;
    int *pval = &ival;
    short sval = (short)ival;
    long lval = (long)ival;
    double dval = (double)ival;
    show_int(ival);
    show_float(fval);
    show_pointer(pval);
    show_short(sval);
    show_long(lval);
    show_double(dval);
}

int main() {
    test_show_bytes(123);
    printf("\n");
    test_show_bytes(32);
    getchar();
}

2.58

int is_little_endian() {
    int a = 1;
    byte_pointer p = &a;
    return p[0];
}

2.59

#include <stdio.h>

int main() {
    int x = 0x89ABCDEF;
    int y = 0x76543210;
    printf("0x%x\n", (0xFF & x) + (0xFFFFFF00 & y));
    system("pause");
}

2.60

#include <stdio.h>

unsigned replace_byte(unsigned x, int i, unsigned char b) {
    char *p = &x;
    p[i] = b;
    return x;
}
int main() {
    printf("0x%x\n", replace_byte(0x12345678, 2, 0xAB));
    printf("0x%x\n", replace_byte(0x12345678, 0, 0xAB));
    //system("pause");
}

2.61

#include <stdio.h>

int main() {
    int x = 0xFFFFFFFF;
    int y = 0x00000000;
    int z = 0x123456FF;
    int w = 0x00123456;
    /*A*/
    printf("%d %d %d %d\n", !~x, !~y, !~z, !~w);
    /*B*/
    printf("%d %d %d %d\n",  !x, !y, !z, !w);
    /*C*/
    printf("%d %d %d %d\n", !~(x | (-1 << 8)), !~(y | (-1 << 8)), !~(z | (-1 << 8)), !~(w | (-1 << 8)));
    /*D*/
    printf("%d %d %d %d\n", !(x >> ((sizeof(int) - 1) << 3)), !(y >> ((sizeof(int) - 1) << 3)), !(z >> ((sizeof(int) - 1) << 3)), !(w >> ((sizeof(int) - 1) << 3)));
    //system("pause");
}

2.62

#include<stdio.h>
int int_shifts_are_arithmetic() {
    return -2 >> 1 == -1;
}

int main() {
    if (int_shifts_are_arithmetic()) {
        printf("int shifts are arithmetic\n");
    }
    else printf("int shifts are not arithmetic\n");
    //system("pause");
    return 0;
}

2.63

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);
unsigned srl(unsigned x, int k) {
    /*Preform shift arithmetically*/
    unsigned xsra = (int)x >> k;

    int sign = (x & INT_MIN) && (k != 0);                  //符号为负且偏移不为0,则为 sign 为 1,否则为 0
    //xsra -= (unsigned int)(sign * (-1 << (w - k)));    //如果 (int)x 是负则从 xsrl 的前面将 k 个 1 换成 0
    /*题目要求不能用乘法,因此把上式换成*/
    xsra -= (unsigned int)(-sign & (-1 << (w - k)));
    return xsra;
}

int sra(int x, int k) {
    /*Perform shift logically*/
    int xsrl = (unsigned)x >> k;

    int sign = (k != 0) && (x & INT_MIN);  //符号为负且偏移不为0
    //xsrl += sign * (-1 << (w - k));     //如果是负则从 xsrl 的前面将 k 个 0 换成 1
    /*题目要求不能用乘法,因此把上式换成下式*/
    xsrl += (unsigned int)(-sign & (-1 << (w - k)));
    return xsrl;
}
int main() {
    printf("%d\n%d\n%d\n%d\n%d\n", sra(123, 0) == 123 >> 0, sra(-100, 0) == -100 >> 0, sra(-2, w - 1) == -2 >> w - 1, srl(48, 0) == 48 >> 0, srl(UINT_MAX, 2) == UINT_MAX >> 2);
    printf("\nsuccess\n");
    system("pause");
    return 0;
}

2.64

/*Return 1 when any odd bit of x equals 1; 0 otherwise. Assume w=32*/
int any_odd_one(unsigned x) {
    unsigned int t = 0x55555555;
    return (x & t) != 0;
}

2.65

2.66

2.67

A. 位移量大于等于位数, C语言对其行为没有定义

B. C.

#include<stdio.h>
/*以下int32 和 int16 仅用来测试,实际使用时可以用int代替*/
__int32 int32_int_size_is_32() {
    __int32 one = 1;
    __int32 set_msb = one << 31;
    __int32 beyond_msb = one << 31;
    beyond_msb <<= 1;
    return set_msb && !beyond_msb;
}

__int16 int16_int_size_is_32() {
    __int16 one = 1;
    __int16 set_msb = one << 15;
    set_msb <<= 15;
    set_msb <<= 1;
    __int16 beyond_msb = one << 15;
    beyond_msb <<= 15;
    beyond_msb <<= 2;
    //printf("%d %d\n", set_msb, beyond_msb);
    return set_msb && !beyond_msb;
}

int main() {
    printf("%d %d\n", int32_int_size_is_32(), int16_int_size_is_32());
    system("pause");
    return 0;
}

2.68

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);         // int 的位数

int lower_one_mask(int n) {
    int k = w - n;
    return ((UINT_MAX << k) >> k);
}

int main() {
    printf("0x%x 0x%x 0x%x\n",lower_one_mask(6), lower_one_mask(17), lower_one_mask(w));
    system("pause");
    return 0;
}

2.69

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);         // int 的位数

unsigned rotate_left(unsigned x, int n) { // 0<=n < w
    int t = x << n;
    int s = x >> w - n;
    return t | s;
}

int main() {
    printf("0x%x 0x%x 0x%x\n", rotate_left(0x12345678, 4), rotate_left(0x12345678, 20), rotate_left(0x12345678, 28));
    system("pause");          
    return 0;
}

2.70

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);         // int 的位数

int fits_bits(int x, int n) {
    int k = w - n;
    int mask = (UINT_MAX << k) >> k;
    int sign = !(x & INT_MIN);                  //求 x 的符号
    int nbits_max = mask >> 1;              // n 位补码所能表示的最大值

    //int nbits_min = -nbits_max - 1;                           // n 位补码所能表示的最小值
    //return (x >= nbits_min) && (x <= nbits_max);  //不能用比较符号所以改用下式

    return (sign &&                         //当 x 为正时
        ((x & nbits_max) == x)) ||      
        (!sign &&                                   //当 x 为负时
        ((-x == (nbits_max + 1)) ||     
        ((-x & nbits_max) == -x)));
}

int main() {
    printf("%d %d %d\n", fits_bits(2, 2), fits_bits(INT_MIN, 32), fits_bits(-1, 1));
    system("pause");          
    return 0;
}

2.71

A. 得到的结果是unsigned,而并非扩展为signed的结果。
B. 使用int,将待抽取字节左移到最高字节,再右移到最低字节即可。

int xbyte(unsigned word, int bytenum){
    int ret = word << ((3 - bytenum)<<3);
    return ret >> 24;
}

2.72

A. >=语句左边始终是 unsigned,无论怎样都大于等于 0
B. 改为 if(maxbytes > 0 && maxbytes - sizeof(val) >= 0)

2.73

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);
int saturating_add(int x, int y) {
    int sum = x + y;
    /*根据书上65页的原理,不过又不能这么“直白”
    if (x > 0 && y > 0 && sum <= 0) {   
        sum = INT_MAX;
    } else if(x < 0 && y < 0 && sum >= 0) {
        sum = INT_MIN;
    }
    */
    int x_sign = !(x & INT_MIN),                                                    //x的符号
        y_sign = !(y & INT_MIN),                                                        //y的符号
        sum_sign = !(sum & INT_MIN);                                            //sum的符号
    int pos = x_sign && y_sign && !sum_sign,                            //正溢出则 pos 为1
        neg = !x_sign && !y_sign && sum_sign;                           //负溢出则 neg 为1
    sum = (sum | -pos) &                                                                //如果正溢出,sum 会变成 INT_MAX,否则不变
        (INT_MAX + (!pos << (w - 1)));      
    sum = (sum & ((!neg << (w - 1)) >> (w - 1))) |                      //如果负溢出,sum 会变成 INT_MIN,否则不变
        (neg << (w - 1));       

    return sum;
}
int main() {
    printf("%d   %d   %d  %d\n", INT_MAX + 1, saturating_add(INT_MAX, 1),  1 + 2, saturating_add(1, 2));
    printf("%d   %d   %d  %d\n", INT_MIN - 1, saturating_add(INT_MIN, -1),  -1 + (-2), saturating_add(-1, -2));
    system("pause");
    return 0;
}

2.74

类似于 2.73

#include<stdio.h>

int tsub_ok(int x, int y) {
    int Y = -y;
    int sum = x + Y;
    int x_sign = !(x & INT_MIN),                                                    //x的符号
        Y_sign = !(Y & INT_MIN),                                                    //y的符号
        sum_sign = !(sum & INT_MIN);                                            //sum的符号
    int pos = x_sign && Y_sign && !sum_sign,                            //正溢出则 pos1
        neg = !x_sign && !Y_sign && sum_sign;                           //负溢出则 neg 为1

    return !pos && !neg;
}

int main() {
    printf("%d   %d\n", tsub_ok(INT_MAX, -1), tsub_ok(1, 2));
    printf("%d   %d\n", tsub_ok(INT_MIN, 1), tsub_ok(-1, -2));
    system("pause");
    return 0;
}

2.75

这个题我不太理解,在 StackOverflow 找到的推导 _ (:з」∠)_

To convert a signed, 2's complement, 32-bit integer to an unsigned 32-bit integer, we add 2³² to its value if it is negative.

signed_high_prod does a signed multiplication and returns bits 63 to 32 of the product. We want unsigned_high_prod to do the same for unsigned multiplication and to make use of signed_high_prod and then compensate for the difference between unsigned and signed multiplication.

Let N(i) = { 1, i < 0
           { 0, i >= 0

Let U(i) = i + N(i)·2³² { −2³¹ <= i < 2³¹ }

Then:

U(x)·U(y) = (x + N(x)·2³²)·(y + N(y)·2³²)
          = x·y + x·N(y)·2³² + N(x)·2³²·y + N(x)·2³²·N(y)·2³²
          = x·y + x·N(y)·2³² + y·N(x)·2³² + N(x)·N(y)·2⁶⁴

⌊U(x)·U(y)/2³²⌋ = ⌊x·y/2³²⌋ + x·N(y) + y·N(x) + N(x)·N(y)·2³²

Since the arithmetic on unsigned, 32-bit integers will be performed modulo 2³², we have:

⌊U(x)·U(y)/2³²⌋ mod 2³²  = (⌊x·y/2³²⌋ + x·N(y) + y·N(x) + N(x)·N(y)·2³²) mod 2³²
                         = (⌊x·y/2³²⌋ + x·N(y) + y·N(x)) mod 2³²

I believe that accounts for the calculations performed by your unsigned_high_prod function.
下面是我自己写的代码:
#include<stdio.h>
#include <inttypes.h>

/*找不到这样的库函数,下面是我自己编撰的,不能保证完全正确性*/
int signed_high_prod(int x, int y) {
    __int64 X = x, Y = y;
    __int64 s = X * Y;
    //printf("%" PRIx64 " * %" PRIx64 "= %" PRIx64 "\n", X, Y, s);
    s >>= 32;
    return s;
}

/*按照推导写出的函数*/
unsigned unsigned_high_prod(unsigned x, unsigned y) {
    unsigned p = (unsigned)signed_high_prod((int)x, (int)y);
    if ((int)x < 0) {
        p += y;
    }
    if ((int)y < 0) {
        p += x;
    }
    return p;
}

int main() {
    printf("%x   %x\n", unsigned_high_prod(0xFFFFFFFF, 0xFFFFFFFF), signed_high_prod(0xFFFFFFFF, 0xFFFFFFFF));
    system("pause");
    return 0;
}

2.76

void *Calloc(size_t nmemb, size_t size) {
    if (nmemb == 0 || size == 0) return NULL;

    int s = nmemb * size;
    if (s / size == nmemb) {        //检测溢出
        void *p = malloc(s);
        memset(p, 0, s);
        return p;
    }
    return NULL;
}

2.77

A. (x << 4) + x
B. x - (x << 3)
C. (x << 6) - (x << 2)
D. (x << 4) - (x << 7)

2.78

int divide_power2(int x, int k) {
    int K = k & (x >> (w - 1));     //如果 x 为负, K 为 k;否则 K 为 0
    x += (1 << K) - 1;                  //如果 K 为 0, x 不变;否则进行偏置
    x >>= k;
    return x;
}

2.79

#include<stdio.h>
#include <string.h> 
int w = 8 * sizeof(int);

/* 2.78 中的函数*/
int divide_power2(int x, int k) {
    int K = k & (x >> (w - 1));     //如果 x 为负, K 为 k;否则 K 为 0
    x += (1 << K) - 1;                  //如果 K 为 0, x 不变;否则进行偏置
    x >>= k;
    return x;
}

int mul3div4(int x) {
    x = (x << 1) + x;
    return divide_power2(x, 2);
}

int main() {
    int o = 0x12345678, t = -o;
    printf("%d %d\n", mul3div4(o) == 3 * o / 4, mul3div4(t) == 3 * t /4);
    system("pause");
    return 0;
}

2.80

这个题目我不太会,从别的博客搬来的

#include<stdio.h>
#include<climits>
#include<assert.h>
int w = 8 * sizeof(int);
/*
* 这个题目非常有意思,要保证不溢出,就要先做除法,也就是先除以4再乘以3
* 在下边中用到了一个非常巧妙的地方,把一个整数进行拆分
*/

/*
* calculate 3/4x, no overflow, round to zero
*
* no overflow means divide 4 first, then multiple 3, diffrent from 2.79 here
*
* rounding to zero is a little complicated.
* every int x, equals f(first 30 bit number) plus l(last 2 bit number)
*
*   f = x & ~0x3
*   l = x & 0x3
*   x = f + l
*   threeforths(x) = f/4*3 + l*3/4
*
* f doesn't care about round at all, we just care about rounding from l*3/4  //这儿我不太明白,可能是因为 f 加了偏置也没有变化
*
*   lm3 = (l << 1) + l
*
* when x > 0, rounding to zero is easy
*
*   lm3d4 = lm3 >> 2
*
* when x < 0, rounding to zero acts like divide_power2 in 2.78
*
*   bias = 0x3    // (1 << 2) - 1
*   lm3d4 = (lm3 + bias) >> 2
*/

int threeforths(int x) {
    int is_neg = x & INT_MIN;

    int f = x & ~0x3;
    int l = x & 0x3;

    int fd4 = f >> 2;
    int fd4m3 = (fd4 << 1) + fd4;

    int lm3 = (l << 1) + l;
    int bias = (1 << 1) + 1;
    (is_neg && (lm3 += bias));
    int lm3d4 = lm3 >> 2;

    return fd4m3 + lm3d4;
}

int main() {
    assert(threeforths(8) == 6);
    assert(threeforths(9) == 6);
    assert(threeforths(10) == 7);
    assert(threeforths(11) == 8);
    assert(threeforths(12) == 9);

    assert(threeforths(-8) == -6);
    assert(threeforths(-9) == -6);
    assert(threeforths(-10) == -7);
    assert(threeforths(-11) == -8);
    assert(threeforths(-12) == -9);
    system("pause");
    return 0;
}

2.81

#include<stdio.h>
#include<climits>

int main() {
    int k = 5, j = 4;
    int A = (-1 << k), B = ~(-1 << (k + j)) - ((1 << j) - 1);
    printf("A. 0x%x\nB. 0x%x\n", A, B);
    system("pause");
    return 0;
}

2.82

A. x 当 x = 0, y = INT_MIN
B. √ 无论是否溢出,其对低32位的运算没有影响
C. √
~x + ~y + 1 = ~x + 1 + ~y + 1 - 1 = -x + -y - 1 = -(x + y) - 1 = ~(x + y) + 1 - 1 = ~(x + y)
D. √ 无符号数和有符号数位级表示相同
E. √ 左移只能在低位引入0,可能会使 x 变小

2.83

A. 令x为无穷序列表示的值,可以得到x*2^k = Y + x。所以 x = Y/(2^k - 1)。
B. (a)5/7, (b)6/15 = 2/5, (c)19/63

2.84

return ((ux << 1) == (uy << 1)) ||      //都为0 +0 -0
        (!sx && sy) ||                  //x 为负, y 为正
        (sx && sy && (ux <= uy)) ||     //都为正
        (!sx && !sy && (ux >= uy));     //都为负

2.85

A.
7.0 = 111.0(二进制); M = 1.11; f = 0.11;
E = 2; Bias = 2^(k - 1) - 1; e = E + Bias = 2 + 2 ^ (k -1) - 1 = 2^(k - 1) + 1;
位表示 = e~f = 10…01 ~ 110…00

B.
e_MAX = 11..110 = 2^k - 2; E_MAX = e_MAX - Bias = 2^(k - 1) - 1;
当 E = n; M = 1.11…; f = 0.11… ;这样才能保证最大而且最低位为 1,即最大奇整数(前提条件是 E_MAX >= n,即指数位数足够大);
此时 e = E + Bias = n + 2^(k - 1) - 1; V = 2^(n+1) - 1;
位表示 = e~f = e ~ 11…11

C.
最小的(正)规格化数: e = 1; E = 1 - 2^(k - 1) + 1 = 2 - 2^(k - 1); M = 1.0; f = 0.0; V = 2^E;
则其倒数:Vr = 2^(-E); Er = -E = 2^(k-1) - 2; Mr = M = 1.0; fr = f = 0.0; er = Er + Bias = 2^k - 3;
位表示 = er~fr = 11…101 ~ 00…00

2.86

便于理解,我加了一列

描述 十进制 二进制表示
最小的正非规格化数 2\^(-61-2\^14) 3.6452e-4951 0\~000…000\~0\~000…001
最小的正规格化数 2\^(-2\^14+2) 3.3621e-4932 0\~000…001\~1\~000…000
最大的规格化数 (2 - 2\^(-63)) * 2\^(2\^14 - 1) 1.1897e+4932 0\~111…110\~1~111..111

2.87

描述 Hex M E V D
-0 0x8000 0 -14 -0 -0.0
最小的>2的值 0x4001 1025/1024 1 1025 * 2^(-9) 2.001953
512 0x6000 1 9 512 512.0
最大的非规格化的数 0x03FF 1023/1024 -14 1023 * 2^(-24) 0.000061
-∞ 0xFC00 —— —— -∞ -∞
十六进制表示为 3BB0 的数 3BB0 123/64 -1 123 * 2^(-7) 0.960938

猜你喜欢

转载自blog.csdn.net/one_of_a_kind/article/details/81315693