• CSAPP:第二章 2.2 & 2.3练习题答案


       

       

    17 ~ 22(略)

       

    23

    A

    w

    (int)((word << 24) >> 24)

    ((int)word << 24) >> 24

    0x00000076

    0x00000076

    0x00000076

    0x87654321

    0x00000021

    0x00000021

    0x000000c9

    0x000000c9

    0xffffffc9

    0xEDCBA987

    0x00000087

    0xffffff87

       

    B func1是做高位截断,func2做符号位扩展.

       

    24(略)
    25

    length-1会有溢出,所以改为 < length
    有符号数和无符号数比较也有问题,当length特别大的时候,得不到正确的结果。
    所以改为:
    unsigned i
    for (i = 0; i < length; ++i)
     

    26

    经典错误了,无符号数计算结果还是无符号数,产生溢出后变成了一个很大的正数。
         int k = ((1u - 2u) > 0);
           printf( "%d, %u",k, (1u - 2u));
    修改很简单 return strlen(s) > strlen(t);
     

    27

    int uadd_ok(unsigned x, unsigned y)
    {
            unsigned sum = x + y;
            return sum > x;
    }
     

    28

    w=4

    x

    x

    -x

    -x

    十六进制

    十进制

    十进制

    十六进制

    0

    0

    0

    0

    5

    5

    11

    B

    8

    8

    8

    8

    D

    13

    3

    3

    F

    15

    1

    1

       

    29

    w = 5, 运算范围为 -16~15
    2^5 = 32

    x

    y

    x + y

    x + y(t5)

    情况

    [10100]

    -12

    [10001]

    -15

    [100101]

    -27

    5

    1 负溢

    [11000]

    -8

    [11000]

    -8

    [110000]

    -16

    -16

    2

    [10111]

    -9

    [01000]

    8

    [11111]

    -1

    -1

    2

    [00010]

    2

    [00101]

    5

     [00111]

    7

    7

    3

    [01100]

    12

    [00100]

    4

     [10000]

    16

    -16

    4 正溢出 

       

    30

    int tadd_ok(int x, int y)
    {
            int sum = x + y;
            if (sum <=0){
                   return x <= 0 || y <= 0;
           } else{
                   return x > 0 || y > 0;
           }
    }

       

    31

    假设发生了正溢出,那么sum = x + y - 2^w
    那么sum - x = (x + y - 2^w) - x = y - 2^w ==> (y - 2^w) mod 2^w == y,所以等式永远相等
    (y<2^(w -1)-1,
    所以y-2^w需要w+1位来表示,那么阶段到w位,就是把w+1位丢掉,就是正好的-2^w)

       

    32

    x = 0x7fffffff; y=0x80000000;的时候会出错。因为补码负数和正数表示的区间不一样。
    char类型为例,x=0x7f=127, y=0x80=-128
    -y = -(-128) = 128,
    但是128超过了补码正数的表示范围,128还是0x80,在补码计算的时候还是-128
    所以计算 x-y的时候 127 - (-128) = 127 + 128 = 255,溢出了
    但是计算 x + (-y)的时候为 127 + (-128) = -1,没有溢出。
    int
     tsub_ok(int x, int y){
            int sub = x - y;
            return (x >= y)?(sub >= 0):(sub < 0);
    }
      

    33

    x

    x

    -x

    -x

    十六进制

    十进制

    十进制

    十六进制

    0

    0

    0

    0

    5

    5

    -5

    B

    8

    -8

    -8

    8

    D

    -3

    3

    3

    F

    -1

    1

    1

       

    34

    w = 3, 2^3 = 8, 2^6=64

    模式

    x

    y

    x*y

    截断的x*y

    无符号

    补码

    [100] = 4

    [100] = -4

    [101] = 5

    [101] = -3

    20 = [010100]

    12 = [001100]

    [100] = 4

    [100] = -4

    无符号

    补码

    [010] = 2

    [010] = 2

    [111] = 7

    [111] = -1

    14 = [001110]

    -2 = [111110]

    [110] = 6

    [110] = -2

    无符号

    补码

    [110] = 6

    [110] = -2

    [110] = 6

    [110] = -2

    36 = [100100]

    4  = [000100]

    [100] = 4

    [100] = -4


    35

    这里用**来代表算术上的乘法,而*是计算机上的乘法指令。
    假设x,y一共有w
    p = x * y
    *
    乘法的规则是截断高位。如果溢出的话,必然至少存在一个t不为零,u > w 使得 x ** y = p + t2^u;
    q = p/x.
    根据取模的运算法则,可以得到 p = x**q + r. 这里r是余数,也就是说|r| < |x|,也就是说r可以使用w位来表示。
    q = y的时候:
    x**y = p + t2^u => p = x **y -t2^u
    p = x**q +r =>
        p  = x**y + r
    那么就得到 r = -t2^u. 因为 u >w,并且r只需要w位就能表示,那么必然t = r =0
    结论如果 p/x ==y 为真可以推导出 t = r = 0, 没有溢出
    如果t = r = 0 也可以推导出没有溢出,p/x ==y为真。
    所以为充分必要条件.
     

    36

    int tmult_ok(int x,int y){
            long long mult = (long long)x * y;
            return !(mult > INT_MAX || mult < INT_MIN);
    }
     

    37

    A 这段代码使用long long unsigned来计算乘法,所以乘法本身不会产生溢出了。但是malloc本身的参数类型是size_t,所以依然会有一个截断的问题。
    B
    使用一个条件判断,如果asize过大的话,函数直接返回NULL
     

    38

    可以计算 2^k次倍数,和2^k + 1的倍数.
     

    39

    如果位置n为最高位,那么x<<n+1就直接溢出了。所以改为 x<<n - x<<m + x<<n
    当然,在n为最高位的时候,只有x=1的时候才不会左移溢出.
    考虑w=4的时候,当n为最高位的时候1000 = 8,那么只有1<<4 = 8是不溢出的。2<<4 = 16, 已经溢出了.
     

    40

    K

    移位

    加法/减法

    表达式

    6

    2

    1

    x<<1 + x<<2  (6 = 2+4)

    31

    1

    1

    x<<6 - x   (31 = 32-1)

    -6

    2

    1

    x<<1 - x<<3 (-6 = 2-8)

    55

    2

    2

    x<<7 - x<<4 - x  (55=64 - 8 - 1)

       

    41

    如果n为最高有效位,那么选择A。因为n+1会溢出。
    其他情况,如果n-m>=3的话,使用形式B,因为计算量少。

       

    42

    int div16(int x)
    {
            return (x + ((x >> 31) & 0x0F)) >> 4;
    }
     

    43

    M=31,N=8
     

    44

    A: 恒为1

    B:
    恒为1

    C:
    假设x = 2^16 + 2^14.
    (2^16 + 2^14) * (2^16 + 2^14)= (2^16)^2 + 2*2^16*2^14 + (2^14)^2 = 2^32 +2^31 + 2^28
    2^32
    被截断舍去了,2^31是符号位,为负数。

    D:
    恒为1

    E: 0x80000000
    ,也就最小的负数,再取负的话会溢出成为负数。

    F
    :把公式2.52.6带进等式就可以证明恒为1

    G:
     x*~y + uy*ux ==>
    x*(-y -1) + uy*ux ==>
    -xy -x + uy*ux ==> -x
    恒为1

  • 相关阅读:
    领域驱动设计ddd
    LayUI
    Kendo框架
    mysql rdms 笔记
    window系统安装mysql
    在VS项目中通过GIT生成版本号作为编译版本号
    在VS项目中使用SVN版本号作为编译版本号
    Oracle与SQL SERVER编程差异分析(入门)
    发布MeteoInfo 3.0
    Tomcat7 安全部署配置修改
  • 原文地址:https://www.cnblogs.com/aoaoblogs/p/2795226.html
Copyright © 2020-2023  润新知