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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM