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.5或2.6帶進等式就可以證明恆為1了
G: x*~y + uy*ux ==>
x*(-y -1) + uy*ux ==>
-xy -x + uy*ux ==> -x
恆為1