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

原文地址:https://www.cnblogs.com/aoaoblogs/p/2795226.html