├── .DS_Store
├── .idea
├── csapp-3e-solutions.iml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── README.md
├── 第三章答案
├── 3.58.c
├── 3.59.c
├── 3.60.c
├── 3.61.c
├── 3.62.c
├── 3.63.c
├── 3.64.c
├── 3.65.c
├── 3.66.c
├── 3.67.c
├── 3.68.c
├── 3.69.c
├── 3.70.c
├── 3.71.c
├── 3.72.c
├── 3.73.c
├── 3.74.c
└── 3.75.c
└── 第二章答案
├── 2.62.c
├── 2.63.c
├── 2.64.c
├── 2.65.c
├── 2.66.c
├── 2.67.c
├── 2.68.c
├── 2.69.c
├── 2.70.c
├── 2.71.c
├── 2.72.c
├── 2.73.c
├── 2.74.c
├── 2.75.c
├── 2.76.c
├── 2.77.c
├── 2.78.c
├── 2.79.c
├── 2.80.c
├── 2.81.c
├── 2.82.c
├── 2.83.c
├── 2.84.c
├── 2.85.c
├── 2.86.c
├── 2.87.c
├── 2.88.c
├── 2.89.c
├── 2.90.c
├── 2.91.c
├── 2.92.c
├── 2.93.c
├── 2.94.c
├── 2.95.c
├── 2.96.c
└── 2.97.c
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agelessman/csapp-3e-solutions/89d955f0aafabb12722d76acb8a8f438d47c7ff4/.DS_Store
--------------------------------------------------------------------------------
/.idea/csapp-3e-solutions.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
162 |
163 |
164 |
165 |
166 | true
167 | DEFINITION_ORDER
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 | 1519292824304
240 |
241 |
242 | 1519292824304
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # csapp-3e-solutions
2 | 深入理解计算机系统第三版作业题答案
3 |
--------------------------------------------------------------------------------
/第三章答案/3.58.c:
--------------------------------------------------------------------------------
1 | /*
2 | * x in %rdi, y in %rsi, z in %rdx
3 | * subq %rdx, %rsi // y - z ==> y
4 | * imulq %rsi, %rdi // x * y ==> x
5 | * movq %rsi, %rax // y ==> %rax
6 | * salq $63, %rax // << 63
7 | * sarq $63, %rax // >> 63
8 | * xorq %rdi, %rax // 这个时候的%rdi已经是x*y ^ %rax
9 | * 因此可以得出结论 (x*y) ^ ((y-z) << 63 >> 63)
10 | */
11 | long decode2(long x, long y, long z) {
12 | return (x * y) ^ ((y - z) << 63 >> 63);
13 | }
--------------------------------------------------------------------------------
/第三章答案/3.59.c:
--------------------------------------------------------------------------------
1 |
2 | /*
3 |
4 | 根据提示:
5 | x = 2^64 * x_h + x_l (x_h表示x的高64位,x_l表示x的低64位)
6 | y = 2^64 * y_h + y_l (y_h表示y的高64位,y_l表示x的低64位)
7 |
8 | x * y = (2^64 * x_h + x_l) * (2^64 * y_h + y_l)
9 | = 2^64 * x_h * 2^64 * y_h + 2^64 * x_h * y_l + x_l * 2^64 * y_h + x_l * y_l
10 |
11 | 在上边这个表达式中2^64 * x_h * 2^64 * y_h明显已经越界,因此舍去,
12 | x * y = 2^64(x_h * y_l + x_l * y_h) + (x_l * y_l)
13 |
14 | 上边的公式很重要,它表达的就是x*y的乘积的样式,根据p = 2^64 *p_h + p_l 再结合上边的公式
15 | 我们得出的结论是:
16 | 2^64(x_h * y_l + x_l * y_h) + (x_l * y_l) = 2^64 *p_h + p_l
17 | 那么2^64 *p_h = 2^64(x_h * y_l + x_l * y_h) + (x_l * y_l) - p_l
18 | p_h = (x_h * y_l + x_l * y_h) + (x_l * y_l)/2^64 - p_l/2^64
19 |
20 | (x_l * y_l)/2^64 表示相乘后右移64位正好是他们相乘后的高64位的值
21 | p_l/2^64 则为0
22 |
23 | 因此我们就把任务简化了,我们接下来看汇编
24 |
25 | dest in %rdi, x in %rsi, y in %rdx
26 |
27 | stroe_prod:
28 | movq %rdx, %rax // %rax = y, 此时y_l = %rax
29 | cqto // 该命令的作用是把%rax中的符号位扩展到%rdx中,此时y_h = %rdx
30 | movq %rsi, %rcx // 这行命令的作用是配合下一行获取x高64位的值
31 | sarq $63, %rcx // 获取x的高64的值x_h = %rcx
32 | imulq %rax, %rcx // 计算y_l * x_h = %rax * %rcx
33 | imulq %rsi, %rdx // 计算y_h * x_l = %rdx * %rsi
34 | addq %rdx, %rcx // 计算x_h * y_l + x_l * y_h的值
35 | mulq %rsi // 该命令是计算%rax * %rsi的值,也就是x_l * y_l的值
36 | addq %rcx, %rdx // 根据上边我们得出的结论,进行相加处理
37 |
38 | */
--------------------------------------------------------------------------------
/第三章答案/3.60.c:
--------------------------------------------------------------------------------
1 | /*
2 | 我们先写出汇编的注释:
3 | x in %rdi, n in %esi
4 | loop:
5 | movl %esi, %ecx // %ecx = n
6 | movl $1, %edx // %edx = 1
7 | movl $0, %eax // %eax = 0
8 | jmp .L2 // 跳转到L2
9 | .L3:
10 | movq %rdi, %r8 // %r8 = x
11 | andq %rdx, %r8 // %r8 &= %rdx
12 | orq %r8, %rax // %rax |= %r8
13 | salq %c1, %rdx // %rdx <<= %cl
14 | .L2:
15 | testq %rdx, %rdx // %rdx & %rdx
16 | jne .L3 // if != jump to .L3
17 |
18 | 根据.L2我们可以得出的结论是如果%rdx的值为0 就继续循环
19 |
20 | .L3中做了什么事呢?
21 | 我们知道%rdx的初始值为1,返回值%rax的值为0,那么.L3中的解释为:
22 | 1. x &= %rdx
23 | 2. %rax |= x
24 | 3. %rdx << n的低8位的值,也是为了保护位移
25 |
26 |
27 | 通过分析,我们就可以得出结论,该函数的目的是得出x中n的倍数的位掩码
28 | 答案:
29 | A:
30 | x --> %rdi
31 | n --> %esi
32 | result --> %rax
33 | mask --> %rdx
34 |
35 | B:
36 | result = 0
37 | mask = 1
38 |
39 | C:
40 | mask != 0
41 |
42 | D:
43 | mask <<= n
44 |
45 | E:
46 | result |= (x & mask)
47 |
48 | F:
49 | 如下函数
50 |
51 | */
52 |
53 | long loop(long x, int n) {
54 | long result = 0;
55 | long mask;
56 | for (mask = 1; mask != 0; mask = mask << n) {
57 | result |= (x & mask);
58 | }
59 | return result;
60 | }
--------------------------------------------------------------------------------
/第三章答案/3.61.c:
--------------------------------------------------------------------------------
1 |
2 | long cread(long *xp) {
3 | return (xp ? *xp : 0);
4 | }
5 |
6 | long cread_alt(long *xp) {
7 | return (!xp ? 0 : *xp);
8 | }
--------------------------------------------------------------------------------
/第三章答案/3.62.c:
--------------------------------------------------------------------------------
1 | typedef enum {MODE_A, MODE_B, MODE_C, MODE_D, MODE_E} mode_t;
2 |
3 | long switch3(long *p1, long *p2, mode_t action) {
4 | long result = 0;
5 | switch(action) {
6 | case MODE_A:
7 | result = *p2;
8 | *p2 = *p1;
9 | break;
10 | case MODE_B:
11 | result = *p1 + *p2;
12 | *p1 = result;
13 | break;
14 | case MODE_C:
15 | *p1 = 59;
16 | result = *p2;
17 | break;
18 | case MODE_D:
19 | result = *p2;
20 | *p1 = result;
21 | result = 27;
22 | break;
23 | case MODE_E:
24 | result = 27;
25 | break;
26 | default:
27 | result = 12;
28 | }
29 |
30 | return result;
31 | }
--------------------------------------------------------------------------------
/第三章答案/3.63.c:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | sub $0x3c, %rsi // %rsi = n - 60
4 | cmp $0x5, %rsi // 比较%rsi : 5
5 | ja 4005c3 // 大于就跳转
6 | jmpq *0x4006f8(,%rsi,8) // 这一行的目的是直接在跳转表中获取地址然后跳转
7 |
8 | // 因此下边这些汇编代码就是对应跳转表中的地址
9 |
10 | 4005a1对应的index为0和2:
11 | lea 0x0(,%rdi,8), %rax // result = 8x
12 |
13 | 4005c3对应的index为1,也就是case 1,通过观察,它用的就是default的指令
14 | 所以case 1 在switch中是缺失的
15 |
16 | 4005aa对应的index为3:
17 | mov %rdi,%rax // result = x
18 | sar $0x3,%rax // result >>= 3
19 | 也就是result = x / 8
20 |
21 | 4005b2对应的index为4:
22 | mov %rdi,%rax // result = x
23 | shl $0x4,%rax // result <<= 4
24 | sub %rdi,%rax // result -= x
25 | mov %rax,%rdi // x = result
26 | 也就是result = x * 15; x = result
27 |
28 | 4005bf对应的index为5:
29 | imul %rdi,%rdi // x *= x
30 |
31 | lea 0x4b(%rdi), %rax // result = 75 + x
32 |
33 | 经过上边的分析,就很容易得出结论了,但是别忘了要把index加上60
34 |
35 | */
36 |
37 | long switch_prob(long x, long n) {
38 | long result = x;
39 | switch(n) {
40 | case 60:
41 | case 62:
42 | result = 8 * x;
43 | break;
44 | case 63:
45 | result = x / 8;
46 | break;
47 | case 64:
48 | result = 15 * x;
49 | x = result;
50 | case 65:
51 | x *= x;
52 | default:
53 | result = 75 + x;
54 | }
55 | return result;
56 | }
--------------------------------------------------------------------------------
/第三章答案/3.64.c:
--------------------------------------------------------------------------------
1 | 设L为数组元素的大小,X_a表示数据的起始地址
2 | &A[i][j][k] = X_a + L(i * S * T + j * T + k)
3 |
4 | 我们再进一步分析汇编代码:
5 | i in %rdi, j in %rsi, k in %rdx, dest in %rcx
6 |
7 | leaq (%rsi,%rsi,2), %rax // %rax = 3j
8 | leaq (%rsi,%rax,4), %rax // %rax = 13j
9 | movq %rdi, %rsi // %rsi = i
10 | salq $6, %rsi // 结合上一条指令,%rsi = i << 6
11 | addq %rsi, %rdi // %rdi = 65i
12 | addq %rax, %rdi // %rdi = 65i + 13j
13 | addq %rdi, %rdx // %rdx = 65i + 13j + k
14 | movq A(,%rdx,8), %rax // %rax = *(A + 8(65i + 13j + k))
15 | movq %rax, (%rcx) // *dest = *(A + 8(65i + 13j + k))
16 | movl $3640, %eax // %rax = 3640
17 |
18 |
19 | 使用A + 8(65i + 13j + k)和最上边的公式对比后发现:
20 | L: 8
21 | T: 13
22 | S: 5
23 |
24 | 要求出R还必须用到3640这个值
25 | R * T * S * L = 3640
26 | R = 3640 / 8 / 13 / 5 = 7
27 |
28 | R: 7
--------------------------------------------------------------------------------
/第三章答案/3.65.c:
--------------------------------------------------------------------------------
1 | 我们先假设M为4,我们假设矩阵A为:
2 |
3 | 1 2 3 4
4 | 5 6 7 8
5 | 9 10 11 12
6 | 13 14 15 16
7 |
8 | 那么在用函数transpose处理之后,矩阵变成了
9 | 1 5 9 13
10 | 2 6 10 14
11 | 3 7 11 15
12 | 4 8 12 16
13 |
14 | 可以看出对矩阵沿着对角线进行了转换。我们继续看汇编代码
15 | 下边的汇编代码只是函数中内循环中的代码
16 |
17 | .L6:
18 | movq (%rdx), %rcx // %rcx = A[i][j]
19 | movq (%rax), %rsi // %rsi = A[j][i]
20 | movq %rsi, (%rdx) // A[i][j] = A[j][i]
21 | movq %rcx, (%rax) // A[j][i] = A[i][j]
22 | addq $8, %rdx // %rdx += 8
23 | addq $120, %rax // %rax += 120
24 | cmpq %rdi, %rax //
25 | jne .L6 //
26 |
27 | 我们很容易就发现,指向A[i][j]的寄存器为%rdx,指向A[j][i]的寄存器为%rax
28 |
29 | 求M最关键的是找出%rax寄存器移动的规律,因为%rdx也就是A[i][j] + 8 就表示右移一位
30 | 而%rax则要移动M * 8位
31 | 因此M = 120 / 8 = 15
32 |
33 | 上边的寄存器%rdi应该放的就是i == j时的A[i][j]的地址
--------------------------------------------------------------------------------
/第三章答案/3.66.c:
--------------------------------------------------------------------------------
1 | 首先我们写出汇编代码的注释:
2 |
3 | n in %rdi, A in %rsi, j in %rdx
4 |
5 | sum_col:
6 | leaq 1(,%rdi,4), %r8 // %r8 = 1 + 4n
7 | leaq (%rdi,%rdi,2), %rax // %rax = 3n
8 | movq %rax, %rdi // %rdi = 3n
9 | testq %rax, %rax // 3n & 3n
10 | jle .L4 // if <= 0 .L4
11 | salq $3, %r8 // %r8 = (1 + 4n) << 3
12 | leaq (%rsi,%rdx,8), %rcx // %rcx = 8j + A
13 | movl $0, %eax // %rax = 0
14 | movl $0, %edx // %rdx = 0
15 | .L3:
16 | addq (%rcx), %rax // %rax += *%rcx
17 | addq $1, %rdx // %rdx += 1
18 | addq %r8, %rcx // %rcx += (1 + 4n) << 3
19 | cmpq %rdi, %rdx // %rdx : 3n
20 | jne .L3
21 | rep; ret
22 | .L4:
23 | movl $0, %eax // %rax = 0
24 | ret
25 |
26 |
27 | 很明显,.L3上边的代码都是为循环准备数据的
28 |
29 | 如果n = 0 那么就直接返回result = 0
30 |
31 | 然后初始化局部变量%rdx保存i的值,%rax保存result的值,%rcx保存每一行j的地址,
32 | 然后进入循环体.L3
33 |
34 | 由%rdx : 3n可以看出打破循环的条件是 i == 3n 推导出:NR(n) = 3n
35 |
36 | 由%rcx += (1 + 4n) << 3可以看出,%rcx每次都移动了一行的宽度,也就是NC(n) = (1 + 4n) << 3
37 |
38 | 答案是:
39 | NR(n) = 3n
40 | NC(n) = (1 + 4n) << 3
41 |
42 |
43 |
--------------------------------------------------------------------------------
/第三章答案/3.67.c:
--------------------------------------------------------------------------------
1 |
2 | 我们先给汇编代码添加注释:
3 |
4 | x in %rdi, y in %rsi, z in %rdx
5 | eval:
6 | subq $104, %rsp // 给栈分配了104个字节的空间
7 | movq %rdx, 24(%rsp) // 把z的值保存在偏移量为24的位置
8 | leaq 24(%rsp), %rax // %rax保存了z的指针
9 | movq %rdi, (%rsp) // 把x的值保存在偏移量为0的位置
10 | movq %rsi, 8(%rsp) // 把y的值保存在偏移量为8的位置
11 | movq %rax, 16(%rsp) // 把z的指针值保存在偏移量为16的位置
12 | leaq 64(%rsp), %rdi // 把偏移量为64的指针赋值给%rdi,当做参数传递给后边的函数
13 | call process
14 |
15 | movq 72(%rsp), %rax // 取出偏移量为72的值赋值给%rax
16 | addq 64(%rsp), %rax // +
17 | addq 80(%rsp), %rax // +
18 | addq $104, %rsp // 恢复栈顶指针
19 | ret
20 |
21 |
22 | process:
23 | movq %rdi, %rax // 把参数保存到%rax
24 | movq 24(%rsp), %rdx // %rdx = &z 这里有点意思,当调用call后会把函数下边代码的地址压入栈中
25 | movq (%rdx), %rdx // %rdx = z
26 | movq 16(%rsp), %rcx // %rcx = y
27 | movq %rcx, (%rdi) // 把y保存到偏移量为64 + 8 = 72的位置
28 | movq 8(%rsp), %rcx // %rcx = x
29 | movq %rcx, 8(%rdi) // 把x保存到偏移量为64 + 8 + 8 = 80的位置
30 | movq %rdx, 16(%rdi) // 把z保存到偏移量为64 + 8 + 16 = 88的位置
31 | ret
32 |
33 |
34 | 通过上边的注释,下边的问题就很清楚了
35 |
36 | A:
37 | ----------- <-- 108
38 |
39 | z
40 | ----------- <-- 24
41 | &z
42 | ----------- <-- 16
43 | y
44 | ----------- <-- 8
45 | x
46 | ----------- <-- %rsp
47 |
48 | B:
49 | 传递了一个相对于%rsp偏移量为64的指针
50 |
51 | C:
52 | 直接使用偏移量来访问的s的元素
53 |
54 | D:
55 | 直接设置偏移量
56 |
57 | E:
58 | ----------- <-- 108
59 |
60 |
61 | ----------- <-- 88
62 | z
63 | ----------- <-- 80
64 | x
65 | ----------- <-- 72
66 | y
67 | ----------- <-- 64 --- %rax
68 |
69 | ----------- <-- 32
70 | z
71 | ----------- <-- 24
72 | &z
73 | ----------- <-- 16
74 | y
75 | ----------- <-- 8
76 | x
77 | ----------- <-- %rsp
78 |
79 | F:
80 | 通过这个例子,我们能够发现,如果把结构作为参数,那么实际传递的会是一个空的位置指针,函数把数据
81 | 存储在这个位置上,同时返回值也是这个指针。
--------------------------------------------------------------------------------
/第三章答案/3.68.c:
--------------------------------------------------------------------------------
1 |
2 | p in %rdi, q in %rsi
3 | setVal:
4 | movslq 8(%rsi), %rax // %rax = *(8 + q)
5 | addq 32(%rsi), %rax // %rax += *(32 + q)
6 |
7 | movq %rax, 184(%rdi) //
8 |
9 |
10 | 这个问题算是非常简单的,由最后一条代码再加上str的结构,我们可以得出这样一个等式
11 | 4 * A * B + space = 184 由于对齐原则是保证8的倍数,分别假设space为7和0
12 | ==> 44 < A * B <= 46
13 |
14 | %rax = *(8 + q) 可以推断出char array[B] 应该总共使用8个字节
15 | 因为需要考虑对齐原则,所以先得出 B <= 8
16 |
17 | short s[A] %rax += *(32 + q)
18 | 我们t占用4个字节 ==> 4 + A * 2 <= 32 - 8 <= 24
19 |
20 | 于是我们有三个公式来做判断:
21 | 44 < A * B <= 46
22 | B <= 8
23 | A <= 10
24 |
25 | 那么A * B 的值只能是45 组合就是 5 * 9
26 | 由于 B <= 8 因此 B = 5 A = 9
27 |
28 | 我们再验证一番,short s[A] 由于对齐原则 占用了20个字节,跟汇编代码一致
29 |
30 | 答案:
31 | A = 9
32 | B = 5
33 |
34 |
--------------------------------------------------------------------------------
/第三章答案/3.69.c:
--------------------------------------------------------------------------------
1 |
2 | i in %rdi, bp in %rsi
3 | test:
4 | mov 0x120(%rsi), %ecx // %rcx = *(288 + bp)
5 | add (%rsi), %ecx // %rcx = *(288 + bp) + *bp
6 | lea (%rdi,%rdi,4), %rax // %rax = 5 * i
7 | lea (%rsi,%rax,8), %rax // %rax = 5 * i * 8 + bp
8 | mov 0x8(%rax), %rdx // %rdx = *((5 * i * 8 + bp) + 8)
9 | movslq %ecx, %rcx
10 | mov %rcx, 0x10(%rax,%rdx,8) // &(16 + %rax + 8 * %rdx) = %rcx
11 | retq
12 |
13 |
14 | 由 %rdx = (5 * i * 8 + bp) + 8 可以推导出 a_struct a[CNT] 每个元素占40个字节,first占8个字节
15 | ==>
16 | CNT = (288 - 8) / 40 ==> CNT = 7
17 |
18 | 本题重点理解%rax 和 %rdx中保存的是什么的值,
19 | %rax中保存的是ap的值,而%rdx中保存的是ap->idx的值,理解了这一层接下来就简单了
20 |
21 | 说明ap->idx保存的是8字节的值,根据 &(16 + %rax + 8 * %rdx) = %rcx 可以得出idx应该是结构体的第一个变量long idx
22 |
23 | 如果结构体占用了40个字节 , 那么数组x应该占用 40 - 8 也就是32个字节,每个元素占8个,可以容纳4个元素
24 |
25 | typedef struct {
26 | long idx;
27 | long x[4];
28 | }a_struct;
29 |
30 |
31 | 这个题目最重要的地方是理解mov 0x8(%rax), %rdx 这段代码,它是求ap->idx的值。
32 |
--------------------------------------------------------------------------------
/第三章答案/3.70.c:
--------------------------------------------------------------------------------
1 |
2 | A:
3 | 0
4 | 8
5 | 0
6 | 8
7 |
8 | B:
9 | e1最多需要16个字节
10 | e2最多需要16个字节
11 | 因此 总共需要16个字节
12 |
13 | C:
14 | up in %rdi
15 | proc:
16 | movq 8(%rdi), %rax // %rax = *(8 + up) 取出偏移量为8的地址
17 | movq (%rax), %rdx // %rdx = *%rax 取出该地址中的值
18 | movq (%rdx), %rdx // 取出指针指向的值
19 | subq 8(%rax), %rdx // 用该值减去 *(%rax + 8)
20 | movq %rdx, (%rdi) //
21 | ret
22 |
23 | 一般来说 如果一个寄存器,比如说%rax 在下边的使用中用到了(%rax),我们就认定该寄存器保存的值为指针
24 |
25 | movq 8(%rdi), %rax %rax保存了up偏移量为8的指针值,在该函数中偏移量为8还是指针的只能是e2的next
26 | ==> %rax = up -> e2.next
27 |
28 | movq (%rax), %rdx %rdx 同样保存的是指针,对(%rax)取值得到的是up下一个unio的指针
29 | ==> %rdx = *(up -> e2.next)
30 |
31 | movq (%rdx), %rdx 这行代码过后,%rdx就不再是指针了,是一个值,但运行之前,%rdx是个指针
32 | ==> %rdx = *(*(up -> e2.next) -> e2.p)
33 |
34 | subq 8(%rax), %rdx 我们知道%rax是个指针 指向next +8后
35 | ==> 8(%rax) = *(up -> e2.next) -> e1.y
36 |
37 | 答案:
38 | up -> e2.x = *(*(up -> e2.next) -> e2.p) - *(up -> e2.next) -> e1.y;
--------------------------------------------------------------------------------
/第三章答案/3.71.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #define BUF_SIZE 12
4 |
5 | void good_echo(void) {
6 | char buf[BUF_SIZE];
7 | while(1) {
8 | /* function fgets is interesting */
9 | char* p = fgets(buf, BUF_SIZE, stdin);
10 | if (p == NULL) {
11 | break;
12 | }
13 | printf("%s", p);
14 | }
15 | return;
16 | }
17 |
18 | int main(int argc, char* argv[]) {
19 | good_echo();
20 | return 0;
21 | }
--------------------------------------------------------------------------------
/第三章答案/3.72.c:
--------------------------------------------------------------------------------
1 | 我们先画一画栈图:
2 |
3 | ----------
4 |
5 |
6 | ---------- <-- %rbp 0
7 |
8 |
9 | ---------- s1 <-- -16
10 | e1
11 | ----------
12 | p
13 | ---------- p
14 | e2
15 | ---------- s2
16 |
17 |
18 | A:
19 | s2 = %rsp - 16 - (-16 & (8n + 30)) 由于s2 = %rsp - 16 所以
20 | s2 = s1 - (-16 & (8n + 30))
21 |
22 | 这里的-16的十六进制表示为0xfffffff0,之所以用& 就是为了求16的整数倍
23 |
24 | B:
25 | p = (s2 + 15) & 0xfffffff0
26 |
27 | C:
28 | s2 = s1 - (0xfffffff0 & (8n + 30)) 根据这个公式
29 | 当n是偶数的时候,我们可以把式子简化为 s2 = s1 - (8 * n + 16)
30 | 当n是奇数的时候,我们可以把式子简化为 s2 = s1 - (8 * n + 24)
31 |
32 | 先求e1最小的情况
33 | e1和e2是对立的关系,要想e1最小,那么e2就要最大,e2最大也就是15,
34 | n是偶数的时候,e1 = 16 - 15 = 1 这个时候s1 % 16 == 1
35 |
36 | e1最大的情况:
37 | e2 == 0 时 e1最大, 当n是奇数的时候,e1 == 24 这个时候s1 % 16 == 0(p中多处了一个8字节)
38 |
39 | D:
40 | s2 确保能够容纳足够的p, p能够保证自身16对齐
--------------------------------------------------------------------------------
/第三章答案/3.73.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef enum {NEG, ZERO, POS, OTHER} range_t;
5 |
6 | range_t find_range(float x) {
7 | __asm__(
8 | "vxorps %xmm1, %xmm1, %xmm1\n\t"
9 | "vucomiss %xmm1, %xmm0\n\t"
10 | "jp .P\n\t"
11 | "ja .A\n\t"
12 | "jb .B\n\t"
13 | "je .E\n\t"
14 | ".A:\n\t"
15 | "movl $2, %eax\n\t"
16 | "jmp .Done\n\t"
17 | ".B:\n\t"
18 | "movl $0, %eax\n\t"
19 | "jmp .Done\n\t"
20 | ".E:\n\t"
21 | "movl $1, %eax\n\t"
22 | "jmp .Done\n\t"
23 | ".P:\n\t"
24 | "movl $3, %eax\n\t"
25 | ".Done:\n\t"
26 | );
27 | }
28 |
29 | int main(int argc, char* argv[]) {
30 | range_t n = NEG, z = ZERO, p = POS, o = OTHER;
31 | assert(o == find_range(0.0/0.0));
32 | assert(n == find_range(-2.3));
33 | assert(z == find_range(0.0));
34 | assert(p == find_range(3.33));
35 | return 0;
36 | }
--------------------------------------------------------------------------------
/第三章答案/3.74.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef enum {NEG, ZERO, POS, OTHER} range_t;
5 |
6 | range_t find_range(float x) {
7 | __asm__(
8 | "vxorps %xmm1, %xmm1, %xmm1\n\t"
9 | "movq $1, %rax\n\t"
10 | "movq $2, %r8\n\t"
11 | "movq $0, %r9\n\t"
12 | "movq $3, %r10\n\t"
13 | "vucomiss %xmm1, %xmm0\n\t"
14 | "cmovg %r8, %rax\n\t"
15 | "cmove %r9, %rax\n\t"
16 | "cmovpq %r10, %rax\n\t"
17 | );
18 | }
19 |
20 | int main(int argc, char* argv[]) {
21 | range_t n = NEG, z = ZERO, p = POS, o = OTHER;
22 | assert(o == find_range(0.0/0.0));
23 | assert(n == find_range(-2.3));
24 | assert(z == find_range(0.0));
25 | assert(p == find_range(3.33));
26 | return 0;
27 | }
--------------------------------------------------------------------------------
/第三章答案/3.75.c:
--------------------------------------------------------------------------------
1 | 这个题考察的是复数的概念
2 |
3 | 复数 = 实数 + 虚数
4 |
5 | 传参的时候,有这样的规律
6 |
7 | (复数1, 复数2, 复数3...) 对应的浮点寄存器就会是:
8 |
9 | %xmm0, %xmm1, %xmm2, %xmm3, %xmm4, %xmm5 ...
--------------------------------------------------------------------------------
/第二章答案/2.62.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | int int_shifts_are_arithmetic() {
4 | int i = -1;
5 | return (i >> 1) == -1;
6 | }
7 |
8 | int main(void) {
9 | printf("%d", int_shifts_are_arithmetic());
10 | }
--------------------------------------------------------------------------------
/第二章答案/2.63.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | unsigned srl(unsigned x, int k) {
4 | int xsrl = (int)x >> k;
5 | int w = 8 * sizeof(int);
6 |
7 | unsigned z = 2 << (w - k -1);
8 | return (z - 1) & xsrl;
9 | }
10 |
11 | int sra(int x, int k) {
12 | int xsra = (unsigned)x >> k;
13 | int w = sizeof(int) << 3;
14 |
15 | unsigned z = 1 << (w - k - 1);
16 | unsigned mask = z - 1;
17 |
18 | unsigned right = xsra & mask;
19 | unsigned left = ~mask & (~(z & xsra) + z);
20 |
21 | return left | right;
22 | }
23 |
24 | int main(void) {
25 | unsigned t1 = srl(100, 2);
26 | unsigned t2 = (unsigned)100 >> 2;
27 | printf("%d----%d \n", t1, t2);
28 |
29 | int t3 = sra(100, 2);
30 | int t4 = 100 >> 2;
31 | printf("%d----%d \n", t3, t4);
32 |
33 | int t5 = sra(-100, 2);
34 | int t6 = -100 >> 2;
35 | printf("%d----%d \n", t5, t6);
36 | }
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/第二章答案/2.64.c:
--------------------------------------------------------------------------------
1 | // 该题目要求,只要奇数位有1,就返回1,否则返回0
2 |
3 | #include
4 | /* Return 1 when any odd bit of x equals 1, 0 otherwise.
5 | Assume w = 32.
6 | */
7 | int any_odd_one(unsigned x) {
8 | return !!(x & 0x55555555);
9 | }
10 |
11 | int main(void) {
12 | int result = any_odd_one((unsigned)5);
13 | printf("The result of 5: %d \n", result);
14 |
15 | int result1 = any_odd_one((unsigned)2);
16 | printf("The result of 2: %d \n", result1);
17 | }
--------------------------------------------------------------------------------
/第二章答案/2.65.c:
--------------------------------------------------------------------------------
1 | // 该题目要求,只要二进制书中1的个数为奇数,就返回1,否则返回0
2 |
3 |
4 | #include
5 | /* Return 1 when x contains an odd nuimber of 1s, 0 otherwise.
6 | Assume w = 32.
7 | */
8 | int odd_ones(unsigned x) {
9 | // 这是第一层的处理,对某一位i而言,通过右移了一位,我们就获取到了i前边的那一位,把他们异或后,
10 | // 得到的位的值为0或者1,1就表示和前边的一位中有奇数个1,0表示有偶数个1.
11 | x ^= (x >> 1);
12 |
13 | // 经过上边的处理后呢,x中每一位的值的意义就不同了,他表示该位和它前边的位1的个数是奇数还是偶数
14 | // 此时我们再右移2位,就获得了i前边的前边的值j,这个值j表示j和前边一位1的个数是奇数还是偶数
15 | // 异或后,的值就便是到j前边,一共四位1的个数是奇数还是偶数
16 | x ^= (x >> 2);
17 |
18 | // 后面的都是按照上边的原理依次类推的
19 |
20 | x ^= (x >> 4);
21 | x ^= (x >> 8);
22 | x ^= (x >> 16);
23 | return x & 1;
24 | }
25 |
26 | int main(void) {
27 | int result = odd_ones((unsigned)5);
28 | printf("The result of 5: %d \n", result);
29 |
30 | int result1 = odd_ones((unsigned)7);
31 | printf("The result of 3: %d \n", result1);
32 | }
--------------------------------------------------------------------------------
/第二章答案/2.66.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | // 1. 先使用或加位移让第一个1的后边都是1
5 | // 2. 然后取非后右移一位后,最右边的1就是我们想要的掩码
6 | // 3. 由于上边得到的那个1就是原值中的第一个1的位置,因此&上原值就清空了1前边的位
7 | int leftmost_one(unsigned x) {
8 | x |= x >> 1;
9 | x |= x >> 2;
10 | x |= x >> 4;
11 | x |= x >> 8;
12 | x |= x >> 16;
13 |
14 | return x & (~x >> 1);
15 | }
16 |
17 | int main(void) {
18 | assert(leftmost_one(0xff00) == 0x8000);
19 | assert(leftmost_one(0x6600) == 0x4000);
20 | return 0;
21 | }
--------------------------------------------------------------------------------
/第二章答案/2.67.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | int int_size_is_32() {
5 | int set_msb = 1 << 31;
6 | int beyond_msb = set_msb << 1;
7 |
8 | return set_msb && !beyond_msb;
9 | }
10 |
11 | int int_size_is_32_for_16bit() {
12 | int set_msb = 1 << 15 << 15 << 1;
13 | int beyond_msb = set_msb << 1;
14 |
15 | return set_msb && !beyond_msb;
16 | }
17 |
18 | int main(void) {
19 | printf("1: %lu \n", sizeof(1));
20 | printf("32: %d \n", int_size_is_32());
21 | printf("16: %d \n", int_size_is_32_for_16bit());
22 | }
--------------------------------------------------------------------------------
/第二章答案/2.68.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | int lower_one_mask(int n) {
5 | int w = sizeof(int) << 3;
6 | return (unsigned)-1 >> (w - n);
7 | }
8 |
9 | int main(void) {
10 | assert(lower_one_mask(6) == 0x3F);
11 | assert(lower_one_mask(17) == 0x1FFFF);
12 | assert(lower_one_mask(32) == 0xFFFFFFFF);
13 | return 0;
14 | }
--------------------------------------------------------------------------------
/第二章答案/2.69.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | unsigned rotate_left(unsigned x, int n) {
5 | int w = sizeof(int) << 3;
6 | unsigned t = x << n;
7 | unsigned t1 = x >> (w - n - 1) >> 1;
8 | return t | t1;
9 | }
10 |
11 | int main(void) {
12 | assert(rotate_left(0x12345678, 4) == 0x23456781);
13 | assert(rotate_left(0x12345678, 20) == 0x67812345);
14 | assert(rotate_left(0x12345678, 0) == 0x12345678);
15 | return 0;
16 | }
--------------------------------------------------------------------------------
/第二章答案/2.70.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | // 如果x的二进制可以用n位表示就返回1,
5 | /*
6 | * Assume w = 8, n = 3
7 | * if x > 0
8 | * 0b00000110 is ok, 0b00001010 is not
9 | * first w-n bits must be 0
10 | * if x < 0
11 | * 0b11111100 is ok, 0b10111100 is not, and 0b11111000 is not yet
12 | * first w-n+1 bits must be 1
13 | */
14 | int fits_bits(int x, int n) {
15 | int w = sizeof(int) << 3;
16 | x >>= n - 1;
17 |
18 | /*
19 | * !(x >> 1) 用于判断x大于0的情况
20 | * !~x 用于判断x小于0的情况
21 | */
22 | return !(x >> 1) || !~x;
23 | }
24 |
25 | int main(void) {
26 | assert(fits_bits(0xFF, 8));
27 | assert(!fits_bits(0xFFFFFF00, 8));
28 | return 0;
29 | }
--------------------------------------------------------------------------------
/第二章答案/2.71.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef unsigned packet_t;
5 |
6 | // 该函数的作用是取出一个字中的某个字节,然后把该字节扩展为有符号整数
7 | // 难点在于如何利用算数右移填充前边的位
8 | // 核心思想就是先把目前字节左移到最高位,然后再利用算数右移
9 | int xbyte(packet_t word, int bytenum) {
10 | int size = sizeof(unsigned);
11 | int shift_left_val = (size - 1 - bytenum) << 3;
12 | int shift_right_val = (size - 1) << 3;
13 | return (int)word << shift_left_val >> shift_right_val;
14 | }
15 |
16 | int main(void) {
17 | assert(xbyte(0xAABBCCDD, 1) == 0xFFFFFFCC);
18 | assert(xbyte(0x00112233, 2) == 0x11);
19 | return 0;
20 | }
--------------------------------------------------------------------------------
/第二章答案/2.72.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | void copy_int(int val, void *buf, int maxbytes) {
7 | if (maxbytes >= (int)sizeof(val)) {
8 | memcpy(buf, (void *)&val, sizeof(val));
9 | }
10 | }
11 |
12 | int main() {
13 | int maxbytes = sizeof(int) * 10;
14 | void *buf = malloc(maxbytes);
15 | int val;
16 |
17 | val = 0x12345678;
18 | copy_int(val, buf, maxbytes);
19 | assert(*(int *)buf == val);
20 | val = 0x11111111;
21 |
22 | val = 0xAABBCCDD;
23 | copy_int(val, buf, 0);
24 | assert(*(int *)buf != val);
25 |
26 | return 0;
27 | }
--------------------------------------------------------------------------------
/第二章答案/2.73.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | // 该函数是饱和加法,当正溢出,取最大整数,负溢出,取最小整数
6 | int saturationg_add(int x, int y) {
7 | int sum = x + y;
8 | int sig_mask = INT_MIN;
9 |
10 | // 如果x > 0 y > 0 sum < 0 正溢出
11 | // 如果x < 0 y < 0 sum > 0 负溢出
12 | int pos_over = !(x & sig_mask) && !(y & sig_mask) && (sum & sig_mask);
13 | int neg_over = (x & sig_mask) && (y & sig_mask) && !(sum & sig_mask);
14 |
15 | (pos_over && (sum = INT_MAX)) || (neg_over && (sum = INT_MIN));
16 |
17 | return sum;
18 | }
19 |
20 |
21 | int main() {
22 | assert(INT_MAX == saturationg_add(INT_MAX, 0x1234));
23 | assert(INT_MIN == saturationg_add(INT_MIN, -0x1234));
24 | assert(0x12 + 0x34 == saturationg_add(0x12, 0x34));
25 |
26 | return 0;
27 | }
--------------------------------------------------------------------------------
/第二章答案/2.74.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | // 该函数用于检查两个整数相减会不会产生溢出
6 | // 这个和上边的题目很相似,可以把x-y看做x+(-y)
7 | int tsub_ok(int x, int y) {
8 | // 当y为最小整数的时候,就产生了溢出,因为任何数减最小数都会溢出
9 | if (y == INT_MIN) {
10 | return 0;
11 | }
12 |
13 | int neg_y = -y;
14 | int sum = x + neg_y;
15 | int pos_over = x > 0 && neg_y > 0 && sum < 0;
16 | int neg_over = x < 0 && neg_y < 0 && sum >= 0;
17 |
18 | return !(pos_over || neg_over);
19 | }
20 |
21 |
22 | int main(int argc, char* argv[]) {
23 | assert(!tsub_ok(0x00, INT_MIN));
24 | assert(tsub_ok(0x00, 0x00));
25 | return 0;
26 | }
27 |
--------------------------------------------------------------------------------
/第二章答案/2.75.c:
--------------------------------------------------------------------------------
1 | /*
2 | 这个问题需要一步一步的进行推导
3 | T2Uw(x)我们把这种写法称为补码转无符号数,那么很容易得出:
4 | (2^w表示2的w次方,为什么当x<0时是这个结果呢,
5 | 其实,补码的负数就是把原来w-1之后的位的结果减去了最高一位的值,最高位的值就是2^w)
6 | if x < 0 => x + 2^w
7 | if x > 0 => x
8 |
9 | 上边的公式很简单,但在使用的时候还要做判断,显然很不科学,我们可以认为T2Uw(x)是一个函数
10 | 接下来就想办法推导出一个表达式来
11 |
12 | 这里省略了一系列的推导过程,得出了这样一个结果"
13 | T2Uw(X)= X + X(w-1)2^w
14 |
15 | 大家看看这个式子跟上边的那个作用一样,x的w-1位就是他的最高位,如果该位的值是1,那么就相当于
16 | x<0的情况,否则就是另一种情况
17 |
18 | 我们假设x`表示x的无符号值
19 | X` = X + X(w-1)2^w
20 |
21 | 我们假设y`表示x的无符号值
22 | Y` = Y + Y(w-1)2^w
23 |
24 | 那么X` * Y` = (X + X(w-1)2^w) * (Y + Y(w-1)2^w)
25 | 如果要把这个计算式展开会很麻烦,我们可以进一步抽象
26 | 设a = X(w-1)2^w, b= Y(w-1)2^w
27 | 则: X` * Y` = X*Y + X*b + Y*a + a*b
28 |
29 | 我们假定有这样一个函数,他的功能是取出无符号数的最高位uh(),因此上边的式子变形为:
30 | uh(X` * Y`) = uh(X*Y + X*b + Y*a + a*b)
31 | = uh(X*Y) + uh(X*b) + uh(Y*a) + uh(a*b)
32 |
33 | 那么X * b 也就是X*b= X*Y(w-1)2^w 他的最高位的值就是X*Y(w-1)2^w / 2^w => X*Y(w-1)
34 | 那么Y * a 也就是Y*a= Y*X(w-1)2^w 他的最高位的值就是Y*X(w-1)2^w / 2^w => Y*X(w-1)
35 | 那么a * b 也就是a*b= X(w-1)2^w * Y(w-1)2^w 他 / 2^w => 0
36 |
37 | ===> uh(X` * Y`) = uh(X*Y) + X*Y(w-1) + Y*X(w-1)
38 |
39 | 上边推理的核心思想就是 无符号X`的补码表示:X + X(w-1)2^w 求高位的/ 2^w 操作
40 | */
41 |
42 | /*
43 | * unsigned-high-prod.c
44 | */
45 | #include
46 | #include
47 | #include
48 |
49 | int signed_high_prod(int x, int y) {
50 | int64_t mul = (int64_t) x * y;
51 | return mul >> 32;
52 | }
53 |
54 | unsigned unsigned_high_prod(unsigned x, unsigned y) {
55 | /* TODO calculations */
56 | int sig_x = x >> 31;
57 | int sig_y = y >> 31;
58 | int signed_prod = signed_high_prod(x, y);
59 | return signed_prod + x * sig_y + y * sig_x;
60 | }
61 |
62 | /* a theorically correct version to test unsigned_high_prod func */
63 | unsigned another_unsigned_high_prod(unsigned x, unsigned y) {
64 | uint64_t mul = (uint64_t) x * y;
65 | return mul >> 32;
66 | }
67 |
68 | int main(int argc, char* argv[]) {
69 | unsigned x = 0x12345678;
70 | unsigned y = 0xFFFFFFFF;
71 |
72 | assert(another_unsigned_high_prod(x, y) == unsigned_high_prod(x, y));
73 | return 0;
74 | }
--------------------------------------------------------------------------------
/第二章答案/2.76.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | void *another_calloc(size_t nmemb, size_t size) {
8 | if (nmemb == 0 || size == 0) {
9 | return NULL;
10 | }
11 |
12 | size_t buff_size = nmemb * size;
13 | if (nmemb == buff_size / size) {
14 | void *ptr = malloc(buff_size);
15 | memset(ptr, 0, buff_size);
16 | return ptr;
17 | }
18 |
19 | return NULL;
20 | }
21 |
22 |
23 | int main() {
24 | void *p;
25 | p = another_calloc(0x1234, 1);
26 | assert(p != NULL);
27 | free(p);
28 |
29 | p = another_calloc(SIZE_MAX, 2);
30 | assert(p == NULL);
31 | free(p);
32 |
33 | return 0;
34 | }
--------------------------------------------------------------------------------
/第二章答案/2.77.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | // K = 17
5 | int A(int x) {
6 | return (x << 4) + x;
7 | }
8 |
9 | // K = -7
10 | int B(int x) {
11 | return x - (x << 3);
12 | }
13 |
14 | // K = 60
15 | int C(int x) {
16 | return (x << 6) - (x << 2);
17 | }
18 |
19 | // K = -112
20 | int D(int x) {
21 | return (x << 4) - (x << 7);
22 | }
23 |
24 | int main() {
25 | int x = 0x12345678;
26 |
27 | assert(A(x) == x * 17);
28 | assert(B(x) == x * -7);
29 | assert(C(x) == x * 60);
30 | assert(D(x) == x * -112);
31 |
32 | printf("Passed.\n");
33 | return 0;
34 | }
--------------------------------------------------------------------------------
/第二章答案/2.78.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | // c语言的除法要求向0取整,除法本质上就是右移操作
6 | int divide_power2(int x, int k) {
7 | int is_neg = x & INT_MIN;
8 | (is_neg && (x = x + (1 << k) - 1));
9 | return x >> k;
10 | }
11 |
12 | int main(int argc, char* argv[]) {
13 | int x = 0x80000007;
14 | assert(divide_power2(x, 1) == x / 2);
15 | assert(divide_power2(x, 2) == x / 4);
16 |
17 | printf("%d", x);
18 | return 0;
19 | }
20 |
--------------------------------------------------------------------------------
/第二章答案/2.79.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | /*
6 | * 在这个题目中的除以4中我们需要注意的是取整问题,因此需要用到题目2.78的函数
7 | */
8 |
9 | int divide_power2(int x, int k) {
10 | int is_neg = x & INT_MIN;
11 | (is_neg && (x = x + (1 << k) -1));
12 | return x >> k;
13 | }
14 |
15 | int mul3div4(int x) {
16 | int mul3 = (x << 1) + x;
17 | return divide_power2(mul3, 2);
18 | }
19 |
20 |
21 | int main() {
22 | int t = 0x12345678;
23 | assert(mul3div4(t) == (t * 3 / 4));
24 | return 0;
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/第二章答案/2.80.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | /*
6 | * 这个题目非常有意思,要保证不溢出,就要先做除法,也就是先除以4再乘以3
7 | * 在下边中用到了一个非常巧妙的地方,把一个整数进行拆分
8 | */
9 |
10 | /*
11 | * calculate 3/4x, no overflow, round to zero
12 | *
13 | * no overflow means divide 4 first, then multiple 3, diffrent from 2.79 here
14 | *
15 | * rounding to zero is a little complicated.
16 | * every int x, equals f(first 30 bit number) plus l(last 2 bit number)
17 | *
18 | * f = x & ~0x3
19 | * l = x & 0x3
20 | * x = f + l
21 | * threeforths(x) = f/4*3 + l*3/4
22 | *
23 | * f doesn't care about round at all, we just care about rounding from l*3/4
24 | *
25 | * lm3 = (l << 1) + l
26 | *
27 | * when x > 0, rounding to zero is easy
28 | *
29 | * lm3d4 = lm3 >> 2
30 | *
31 | * when x < 0, rounding to zero acts like divide_power2 in 2.78
32 | *
33 | * bias = 0x3 // (1 << 2) - 1
34 | * lm3d4 = (lm3 + bias) >> 2
35 | */
36 |
37 | int threeforths(int x) {
38 | int is_neg = x & INT_MIN;
39 |
40 | int f = x & ~0x3;
41 | int l = x & 0x3;
42 |
43 | int fd4 = f >> 2;
44 | int fd4m3 = (fd4 << 1) + fd4;
45 |
46 | int lm3 = (l << 1) + l;
47 | int bias = (1 << 1) + 1;
48 | (is_neg && (lm3 += bias));
49 | int lm3d4 = lm3 >> 2;
50 |
51 | return fd4m3 + lm3d4;
52 | }
53 |
54 | int main(int argc, char* argv[]) {
55 | assert(threeforths(8) == 6);
56 | assert(threeforths(9) == 6);
57 | assert(threeforths(10) == 7);
58 | assert(threeforths(11) == 8);
59 | assert(threeforths(12) == 9);
60 |
61 | assert(threeforths(-8) == -6);
62 | assert(threeforths(-9) == -6);
63 | assert(threeforths(-10) == -7);
64 | assert(threeforths(-11) == -8);
65 | assert(threeforths(-12) == -9);
66 | return 0;
67 | }
--------------------------------------------------------------------------------
/第二章答案/2.81.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | int A(int k) {
6 | return -1 << k;
7 | }
8 |
9 | int B(int k, int j) {
10 | return ~A(k) << j;
11 | }
12 |
13 |
14 | int main(int argc, char* argv[]) {
15 | assert(A(8) == 0xFFFFFF00);
16 | assert(B(16, 8) == 0x00FFFF00);
17 |
18 | printf("%d", -INT_MIN);
19 | return 0;
20 | }
--------------------------------------------------------------------------------
/第二章答案/2.82.c:
--------------------------------------------------------------------------------
1 | /*
2 | * 2.82.c
3 | */
4 | #include
5 | #include
6 | #include
7 | #include "lib/random.h"
8 |
9 | // 强调的是这个推导的过程
10 |
11 | /* broken when x is INT_MIN */
12 | int A(int x, int y) {
13 | return (x < y) == (-x > -y);
14 | }
15 |
16 | /*
17 | * right
18 | *
19 | * ((x + y) << 4) + y - x
20 | * =>
21 | * x << 4 - x + y << 4 + y
22 | * =>
23 | * x*16 - x + y*16 + y
24 | * whether overflow or not, =>
25 | * x*15 + y*17
26 | */
27 | int B(int x, int y) {
28 | return ((x + y) << 4) + y - x == 17 * y + 15 * x;
29 | }
30 |
31 | /*
32 | * right
33 | *
34 | * ~x + ~y + 1
35 | * =>
36 | * ~x + 1 + ~y + 1 - 1
37 | * =>
38 | * -x + -y - 1
39 | * =>
40 | * -(x + y) - 1
41 | * =>
42 | * ~(x + y) + 1 - 1
43 | * =>
44 | * ~(x + y)
45 | */
46 | int C(int x, int y) {
47 | return ~x + ~y + 1 == ~(x + y);
48 | }
49 |
50 | /*
51 | * right
52 | *
53 | * (ux - uy) == -(unsigned) (y - x)
54 | * =>
55 | * -(ux - uy) == (unsigned) (y - x)
56 | * =>
57 | * (ux - uy) == (unsigned) (x - y)
58 | */
59 | int D(int x, int y) {
60 | unsigned ux = (unsigned) x;
61 | unsigned uy = (unsigned) y;
62 |
63 | return (ux - uy) == -(unsigned) (y - x);
64 | }
65 |
66 | /*
67 | * right
68 | *
69 | * x >> 2 << 2
70 | * =>
71 | * x & ~0x3
72 | * =>
73 | * x - num(00/01/10/11)
74 | * =>
75 | * ((x >> 2) << 2) <= x
76 | */
77 | int E(int x, int y) {
78 | return ((x >> 2) << 2) <= x;
79 | }
80 |
81 | int main(int argc, char* argv[]) {
82 | init_seed();
83 | int x = random_int();
84 | int y = random_int();
85 |
86 | assert(!A(INT_MIN, 0));
87 | assert(B(x, y));
88 | assert(C(x, y));
89 | assert(D(x, y));
90 | assert(E(x, y));
91 | return 0;
92 | }
--------------------------------------------------------------------------------
/第二章答案/2.83.c:
--------------------------------------------------------------------------------
1 | /*
2 | * A:
3 | * 这个问题的关键是找到y,k 和整数x的关系
4 | * 我们假设这个整数是x
5 | * 那么x = 0.yyyyyyyy... 这个时候是无法得出结果的,并没有用到k
6 | * 那么要想用到k,我们把x左移k为 x << k = y.yyyyyyyy...
7 | * 上边的那个表达式中y.yyyyyyyyy... = Y + x
8 | * 因此得出 x << k = Y + x === > x << k - x = Y == > x = Y/(2^k - 1)
9 |
10 |
11 | * B:
12 | * y = 101 == > k=3 Y=5 x=5/7
13 | * y = 0110 == > k=4 Y=6 x=6/15
14 | * y = 010011 == > k=6 Y=19 x=19/63
15 | */
--------------------------------------------------------------------------------
/第二章答案/2.84.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | unsigned f2u(float x) {
5 | return *(unsigned *)&x;
6 | }
7 |
8 | int float_le(float x, float y) {
9 | unsigned ux = f2u(x);
10 | unsigned uy = f2u(y);
11 |
12 | unsigned sx = ux >> 31;
13 | unsigned sy = uy >> 31;
14 |
15 | return sx == sy ? (sx == 0 ? ux <= uy : ux >= uy) : sx > sy;
16 | }
17 |
18 |
19 | int main() {
20 | assert(float_le(+0, -0));
21 | assert(float_le(0, 3));
22 | assert(float_le(-4.12, -0));
23 | assert(float_le(-4, 4));
24 |
25 | return 0;
26 | }
--------------------------------------------------------------------------------
/第二章答案/2.85.c:
--------------------------------------------------------------------------------
1 |
2 | A:
3 | bias = 2^(k-1) - 1 =
4 | v = 2^E + M
5 |
6 | 7.0 = 111.000 = 1.11000x2^2
7 |
8 | E = 2 = e - bias ==> e = E + bias = 2 + bias = 1 + 2^(k-1) ==> 0 1000...001 1100...
9 |
10 |
11 | B:
12 | 能够描述的最大的奇整数的位应该是111111......
13 | 而浮点数表示为1.111111...*2^n的样式,小数点后边应该有n个1 得到这些,我们就能计算出该浮点数的二进制表示
14 | 因此最大的奇整数位11111... 有n+1个1 也就是2^(n+1) - 1
15 | E = n ==> e = E + bias = n + bias
16 | ==> 0 n + bias 11111...
17 |
18 | C:
19 | 要想得到最小的规格数,M必须是1.00...的样式 E = 1 - bias
20 | V = 2^(1-bias) 取倒数 ==> V = 2^(bias-1) ==> E = bias - 1
21 | e = bias + E ==> e = 2bias -1 = 2(2^(k-1) - 1) - 1 = 2^k -3
22 | ==> 0 1111...101 000000
--------------------------------------------------------------------------------
/第二章答案/2.86.c:
--------------------------------------------------------------------------------
1 |
2 | 第一行答案:
3 | 最小的正非规格化数,要满足一下几个条件
4 | 1. 符号位为1
5 | 2. 阶码位全部为0
6 | 3. 单独的整数位为0
7 | 4. 小数位最后一位为1,其他都为0
8 | 得出的结论是: 0 000..00(15位) 0 000..01(63位)
9 |
10 | 偏量bias = 2^(k-1) - 1 = 2^(15-1) - 1 = 2^14 - 1
11 | E = 1 - bias = 1 - 2^14 + 1 = 2 - 2^14
12 | V = M * 2^E = 2^(-63) * 2^(2 - 2^14) = 2^(-63 + 2 - 2^14) = 2^(-61-2^14)
13 |
14 |
15 |
16 | 第二行答案:
17 | 最小的正规格数,满足下边几个条件
18 | 1. 符号位为0
19 | 2. 阶码位为1
20 | 3. 按照该题目要求,单独的整数位为1
21 | 4. 小数位全是0
22 | 得出的结论是: 0 000..01(15位) 1 000..00(63位)
23 |
24 | 偏量bias = 2^(k-1) - 1 = 2^(15-1) - 1 = 2^14 - 1
25 | E = e - bias = 1 - 2^14 + 1 = 2 - 2^14
26 | V = M * 2^E = 1 * 2^(2 - 2^14)
27 |
28 |
29 |
30 | 第三行答案:
31 | 最大的规格数,满足下边几个条件
32 | 1. 符号位为0
33 | 2. 阶码位全为1
34 | 3. 按照该题目要求,单独的整数位为1
35 | 4. 小数位全是1
36 | 得出的结论是: 0 111..10(15位) 1 111..11(63位)
37 |
38 | 偏量bias = 2^(k-1) - 1
39 | E = e - bias = 2^15 - 2 - bias
40 | V = M * 2^E = M * 2^(2^15 - 2 - bias) = M * 2^(2^14 * 2 - 2 - bias)
41 | = M * 2^(2bias - bias) = M * 2^bias
42 | 此时M = 1 + (1 - 2^-63) = 2 - 2^-63
43 | 得出最终的结果是:2^bias * (2 - 2^-63)
--------------------------------------------------------------------------------
/第二章答案/2.87.c:
--------------------------------------------------------------------------------
1 | -0:
2 | 首先尾数M必须为0
3 | 阶码可以设置成00000 因此E = 1 - bias = 1 - 15 = -14
4 | 得到的位模式为:1 00000 0000000000 ==> 0x8000
5 |
6 |
7 | 最小的>2的值:
8 | 由M * 2^E ==> E = 1 M = 1.0000000001
9 | E = e - bias ==> e = E + bias = 16 ==> e = 100000
10 | M的值为2^-10 + 1 = 1025/1024
11 | 得到的位模式为:0 10000 0000000001 ==> 0x4001
12 | V = 1025/1024 * 2 = 1025/512
13 |
14 |
15 | 512:
16 | M = 1 E = 9 = e - bias ==> e = 9 + 15 = 24 ==> 11000
17 | 得到的位模式为:0 11000 0000000000 ==> 0x6000
18 |
19 |
20 | 最大的非规格化数:
21 | 非规格化数表示阶码位都是0 E= 1 - bias = -14
22 | M 1023/1024
23 | 得到的位模式为:0 00000 1111111111 ==> 0x03FF
24 |
25 |
26 | -oo:
27 | 1 11111 0000000000 ==> 0xFC00
28 |
29 |
30 | 十六进制表示为3BB0:
31 | 先把这个数展开:0011 1011 1011 0000 ==> 0 01110 1110110000
32 | e = 14 E = e - bias = 14 - 15 = -1
33 | M = 2^-1 + 2^-2 + 2^-3 + 2^-5 + 2^-6 = 59/64
34 | V = M * 2^E = 59/64 * 2^-1 = 59/128
--------------------------------------------------------------------------------
/第二章答案/2.88.c:
--------------------------------------------------------------------------------
1 | 注意:如果是规格化的M = 1 + f 非规格化M = f
2 |
3 | 0 10110 101 :
4 | A:
5 | E = 22 - 15 = 7 V = (2^-1 + 2^-3 + 1) * 2^7 = 13 * 2^4
6 | B:
7 | 通过观察,我们发现,先保持小数位不变,求阶码,如果不行,在改变小数位
8 | 因此B的 0 1110 1010 V = 13 * 2^4
9 |
10 |
11 | 1 00111 110:
12 | A:
13 | E = 7 - 15 = -8 (2^-1 + 2^-2 + 1) * 2^-8 = 7/4 * 2^-8 = -7/2^10
14 | B:
15 | 1 0011 1110 ==> M = 1 + 2^-1 + 2^-2 + 2^-3 = 15/8
16 | ==> 2^E = (7/2^10) / (15/8) = 7/15 / 2^7 约等于2^-1*2^-7 = 2^-8
17 | 我们看看2^E的范围 2^-6 ~ 2^14
18 | 由于上边计算的2^-8不在这个范围中,因此需要调整阶码的值
19 | 先从最小的开始,设阶码为2^-6 那么 7/2^10 / 2^-6 = 7 / 16
20 | ==> (1/16 + 2/16 + 4/16) ==> (1/16 + 1/8 + 1/4) ==> (2^-4 + 2^-3 + 2^-2)
21 | 因此B的 0 0000 0111
22 |
23 |
24 | 0 00000 101:
25 | A:
26 | E = 1 - 15 = -14 V = (2^-1 + 2^-3) * 2^-14 = 5 * 2^-3 * 2^-14 = 5 * 2^-17 = 5/2^17
27 | 假设使用101作为尾数,那么M = (2^-1 + 2^-3 + 1) = 13 * 2^-3
28 | 2^E = V/M = 5/2^17 / (13 * 2^-3) = 5/17 * 2^-17 * 2^3 = 5/17 * 2^-14 显然你在范围之内
29 | 先从最小的开始,设阶码为2^-6 那么 5/2^17 / 2^-6 = 5 * 2^-11 显然B无法表示这个小数值
30 | 取一个最近似的 0 0000 0000
31 |
32 |
33 | 1 11011 000:
34 | A:
35 | E = 27 - 15 = 12 V = 2^12 取- 得-2^12
36 | B:
37 | 由于这个值比较大,因此阶码取最大值1110 e = 14 E = e - 7 = 14 - 7 = 7 这样才能计算M的最小值
38 | M = 2^12 / 2^7 = 2^5
39 | 显然M的值无法表示,因此阶码我们这次使用 1111 -oo
40 | 1 1111 0000
41 |
42 |
--------------------------------------------------------------------------------
/第二章答案/2.89.c:
--------------------------------------------------------------------------------
1 | /*
2 | * 2.89.c
3 | */
4 | #include
5 | #include
6 | #include
7 | #include "lib/random.h"
8 |
9 | /*
10 | * most important thing is that all double number come from ints
11 | */
12 |
13 | /* right */
14 | int A(int x, double dx) {
15 | return (float)x == (float)dx;
16 | }
17 |
18 | /* wrong when y is INT_MIN */
19 | int B(int x, double dx, int y, double dy) {
20 | return dx-dy == (double)(x-y);
21 | }
22 |
23 | /* right */
24 | int C(double dx, double dy, double dz) {
25 | return (dx+dy)+dz == dx+(dy+dz);
26 | }
27 |
28 | /*
29 | * wrong
30 | *
31 | * FIXME I don't know what conditions cause false
32 | */
33 | int D(double dx, double dy, double dz) {
34 | return (dx*dy)*dz == dx*(dy*dz);
35 | }
36 |
37 | /* wrong when dx != 0 and dz == 0 */
38 | int E(double dx, double dz) {
39 | return dx/dx == dz/dz;
40 | }
41 |
42 | int main(int argc, char* argv[]) {
43 | init_seed();
44 |
45 | int x = random_int();
46 | int y = random_int();
47 | int z = random_int();
48 | double dx = (double)x;
49 | double dy = (double)y;
50 | double dz = (double)z;
51 |
52 | printf("%x %x %x\n", x, y, z);
53 |
54 | assert(A(x, dx));
55 | assert(!B(0, (double)(int)0, INT_MIN, (double)(int)INT_MIN));
56 | assert(C(dx, dy, dz));
57 | /* magic number, brute force attack */
58 | assert(!D((double)(int)0x64e73387, (double)(int)0xd31cb264, (double)(int)0xd22f1fcd));
59 | assert(!E(dx, (double)(int)0));
60 | return 0;
61 | }
--------------------------------------------------------------------------------
/第二章答案/2.90.c:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * fpwr2.c
4 | */
5 | #include
6 | #include
7 | #include
8 |
9 | float u2f(unsigned x) {
10 | return *(float*) &x;
11 | }
12 |
13 | /* 2^x */
14 | float fpwr2(int x) {
15 | /* Result exponent and fraction */
16 | unsigned exp, frac;
17 | unsigned u;
18 |
19 | /* 因为2^x 是大于0的,因此我们首先要确定浮点数能够表示的正非规格化数的最小值是
20 | 0 00000000 00000...001 ==> 2^-23 * 2^(1-bias) = 2^-23 * 2^(1-(2^7 - 1))
21 | = 2^-23 * 2^(2-2^7)) = 2^(2 - 2^7 -23) = 2 - 128 - 23 = -149
22 |
23 | */
24 | if (x < 2-pow(2,7)-23) {
25 | /* too small. return 0.0 */
26 | exp = 0;
27 | frac = 0;
28 | } else if (x < 2-pow(2,7)) {
29 | /* Denormalized result */
30 | /* 求出最小的规格化数
31 | 0 00000001 00000...000
32 | E = 1 - 2^7 + 1 = 2 - 2^7 = -126 */
33 |
34 | exp = 0;
35 | /* 这段代码块求的值应该是非规格化数范围内的值
36 | 根据 V = M * 2^E V = 2^x ==> 2^x = M * 2^E
37 | frac = M = 2^x / 2^E
38 | E = 1 - bias = 2-2^7
39 | frac = 2^(x - (2 - 2^7)) 这个是frac的值,但是我们如何获得它的位模式呢?
40 | 我们知道0 00000000 00000...001 最后边这个1对应的值是2^-23 也就是说
41 | 小数位的值和他的位模式有一个对应关系,我们只要求出frac是最后这个1(2^-23)的多少
42 | 倍,然后1 << 这个倍数就可以了,这样就得到了frac的位模式
43 | */
44 | frac = 1 << (unsigned)(x - (2-pow(2,7)-23));
45 | } else if (x < pow(2,7)-1+1) {
46 | /* Normalized result */
47 | /* 11111111 2^8 -1 - (2^7 - 1) ==> 2^8 - 2^7 -1 + 1 ==> 2^7
48 | 因此求exp 就等于求e e = E + bias = x + (2^7 - 1)
49 | */
50 | exp = pow(2,7)-1+x;
51 | frac = 0;
52 | } else {
53 | /* Too big, return +oo */
54 | exp = 0xFF;
55 | frac = 0;
56 | }
57 |
58 | /* pack exp and frac into 32 bits */
59 | u = exp << 23 | frac;
60 | /* Result as float */
61 | return u2f(u);
62 | }
63 |
64 | int main(int argc, char* argv[]) {
65 | assert(fpwr2(0) == powf(2,0));
66 | assert(fpwr2(100) == powf(2,100));
67 | assert(fpwr2(-100) == powf(2,-100));
68 | assert(fpwr2(10000) == powf(2,10000));
69 | assert(fpwr2(-10000) == powf(2,-10000));
70 | return 0;
71 | }
--------------------------------------------------------------------------------
/第二章答案/2.91.c:
--------------------------------------------------------------------------------
1 | A:
2 | 0x40490FDB 展开后 0100 0000 0100 1001 0000 1111 1101 1011
3 | 换成小数的位模式: 0 10000000 10010010000111111011011
4 |
5 | 由于2^E = 2 V = 2M 我们知道M = 1.10010010000111111011011 那么2m
6 | 就相当于 << 1 得到:11.0010010000111111011011
7 |
8 | B:
9 | 在问题2.83中我们得出这么一个公式:x = Y/(2^k - 1)
10 | 在本题中 x = 1/7 也就是说Y = 1 k = 3 说明Y是3位且值为1 因此就是001
11 | 所以最终的答案是11.001001001...(001)
12 |
13 | C:
14 | 十进制小数转二进制数:“乘以2取整,顺序排列”(乘2取整法)
15 | 223/71 = 3.140845070422535 小数部分:0.140845070422535
16 | 0.140845070422535 * 2 = 0.28169014084507 ----- 取整 ----- 0
17 | 0.28169014084507 * 2 = 0.563380281690141 ----- 取整 ----- 0
18 | 0.563380281690141 * 2 = 1.126760563380282 ----- 取整 ----- 1
19 | 0.126760563380282 * 2 = 0.253521126760563 ----- 取整 ----- 0
20 | 0.253521 * 2 = 0.507042 ----- 取整 ----- 0
21 | 0.507042 * 2 = 1.014084 ----- 取整 ----- 1
22 | 0.014084 * 2 = 0.028168 ----- 取整 ----- 0
23 | 0.028168 * 2 = 0.056336 ----- 取整 ----- 0
24 | 0.056336 * 2 = 0.112672 ----- 取整 ----- 0
25 | 0.112672 * 2 = 0.225344 ----- 取整 ----- 0
26 | 因此在第9位就不同了
--------------------------------------------------------------------------------
/第二章答案/2.92.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef unsigned float_bits;
5 |
6 | float_bits float_negate(float_bits f) {
7 | unsigned sig = f >> 31;
8 | unsigned exp = f >> 23 & 0xFF;
9 | unsigned frac = f & 0x7FFFFF;
10 |
11 | int is_nan = (exp == 0xFF && frac != 0);
12 | if (is_nan) {
13 | return f;
14 | }
15 |
16 | return ~sig << 31 | exp << 23 | frac;
17 | }
18 |
19 | int main() {
20 | printf("%u", float_negate(32.0));
21 | assert(float_negate(32.0) == -32.0);
22 | return 0;
23 | }
--------------------------------------------------------------------------------
/第二章答案/2.93.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef unsigned float_bits;
5 |
6 | float_bits float_absval(float_bits f) {
7 | unsigned exp = f >> 23 & 0xFF;
8 | unsigned frac = f & 0x7FFFFF;
9 |
10 | int is_nan = (exp == 0xFF && frac != 0);
11 | if (is_nan) {
12 | return f;
13 | }
14 |
15 | return 0 << 31 | exp << 23 | frac;
16 | }
17 |
18 | int main() {
19 | printf("%u\n", float_absval(32.0));
20 | return 0;
21 | }
--------------------------------------------------------------------------------
/第二章答案/2.94.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef unsigned float_bits;
5 |
6 | /*
7 | * 要想实现浮点数*2,可以这么考虑 V = M * 2^E
8 | * 当浮点数是规格数的时候,我们只需要改变E就行了,E = e - bias ==> 相当于给e的值+1
9 | * 但是+1有个特殊情况,要是e的位模式为11111110 +1 就需要特殊处理
10 | * 如果是非规格数, 那么 2^E就是固定的值,我们只能改变M的大小,*2就相当于把小数位左移一位
11 | */
12 |
13 | float_bits float_twice(float_bits f) {
14 | unsigned sig = f >> 31;
15 | unsigned exp = f >> 23 & 0xFF;
16 | unsigned frac = f & 0x7FFFFF;
17 |
18 | int is_nan_or_oo = (exp == 0xFF);
19 | if (is_nan_or_oo) {
20 | return f;
21 | }
22 |
23 | if (exp == 0) {
24 | frac <<= 1;
25 | } else if (exp == 0xFE) {
26 | exp = 0xFF;
27 | frac = 0;
28 | } else {
29 | exp += 1;
30 | }
31 |
32 | return sig << 31 | exp << 23 | frac;
33 | }
34 |
35 | int main() {
36 | printf("%u\n", float_twice(32.22));
37 | return 0;
38 | }
--------------------------------------------------------------------------------
/第二章答案/2.95.c:
--------------------------------------------------------------------------------
1 | /*
2 | * float-half.c
3 | */
4 | #include
5 | #include
6 |
7 | typedef unsigned float_bits;
8 |
9 | float_bits float_half(float_bits f) {
10 | unsigned sig = f >> 31;
11 | unsigned rest = f & 0x7FFFFFFF;
12 | unsigned exp = f >> 23 & 0xFF;
13 | unsigned frac = f & 0x7FFFFF;
14 |
15 | int is_NAN_or_oo = (exp == 0xFF);
16 | if (is_NAN_or_oo) {
17 | return f;
18 | }
19 |
20 | /*
21 | * 这里就用到了向偶数取整的知识,在下边的注释中描述的很详细
22 | * 那么如何理解取整呢,我们假设这个被右移出去的位为a,那么a就有可能是1或者0,如果是0,那么我们
23 | 就不需要取整,如果是1,我们可以这么想:1111.a 这个a如果是1,折算成小数就是0.5 因此是需要
24 | 取整的,它前边的那一位如果是0,表示已经是偶数了,就舍弃a 如果是1,要向上取整,在未右移之前+1就可以了
25 | */
26 |
27 | /*
28 | * round to even, we care about last 2 bits of frac
29 | *
30 | * 00 => 0 just >>1
31 | * 01 => 0 (round to even) just >>1
32 | * 10 => 1 just >>1
33 | * 11 => 1 + 1 (round to even) just >>1 and plus 1
34 | */
35 | int addition = (frac & 0x3) == 0x3;
36 |
37 | if (exp == 0) {
38 | /* Denormalized */
39 | frac >>= 1;
40 | frac += addition;
41 | } else if (exp == 1) {
42 | /* Normalized to denormalized */
43 | rest >>= 1;
44 | rest += addition;
45 | exp = rest >> 23 & 0xFF;
46 | frac = rest & 0x7FFFFF;
47 | } else {
48 | /* Normalized */
49 | exp -= 1;
50 | }
51 |
52 | return sig << 31 | exp << 23 | frac;
53 | }
54 |
--------------------------------------------------------------------------------
/第二章答案/2.96.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | /*
5 | 我们首先考虑作为浮点数f能表示的最大的合法的整数是多少?
6 | V = M * 2^E E = e - bias 由这两个公式可知E越大越好也就是e越大越好
7 | e ==> 11111110 不能是11111111,
8 | 我们再考虑一个范围 0 <= f < 1 如果f在这个范围中,那么它的值就直接取0
9 | 我们要找出这个范围的浮点位模式,0:0 00000000 00000000000000000000000
10 | 1:0 01111111 00000000000000000000000
11 | 在上边的这个空间的值直接取0就行
12 |
13 | 那么f能表示的最大的合法的规格数是 0 11111110 111111111111111111111111
14 | 超过这个数的就成为越界了
15 |
16 | 如果在这个范围内:
17 | E = exp - bias;
18 |
19 | 我们知道M的值的二进制小数是1.xxxxx... 但是下边M的值明显是做了<<23操作的,因此后边就要用E- 23
20 | M = frac | 0x800000;
21 | f = M * 2^E 根据这个公式,向0取整
22 |
23 | if (E > 23) {
24 | num = M << (E - 23);
25 | } else {
26 | num = M >> (23 - E);
27 | }
28 |
29 | */
30 |
31 |
32 | /*
33 | * Compute (float) f
34 | * If conversion cause overflow or f is NaN, return 0x80000000
35 | */
36 | int float_f2i(float_bits f) {
37 | unsigned sig = f >> 31;
38 | unsigned exp = f >> 23 & 0xFF;
39 | unsigned frac = f & 0x7FFFFF;
40 | unsigned bias = 0x7F;
41 |
42 | int num;
43 | unsigned E;
44 | unsigned M;
45 |
46 | /*
47 | * consider positive numbers
48 | *
49 | * 0 00000000 00000000000000000000000
50 | * ===>
51 | * 0 01111111 00000000000000000000000
52 | * 0 <= f < 1
53 | * get integer 0
54 | *
55 | * 0 01111111 00000000000000000000000
56 | * ===>
57 | * 0 (01111111+31) 00000000000000000000000
58 | * 1 <= f < 2^31
59 | * integer round to 0
60 | *
61 | * 0 (01111111+31) 00000000000000000000000
62 | * ===>
63 | * greater
64 | * 2^31 <= f < oo
65 | * return 0x80000000
66 | */
67 | if (exp >= 0 && exp < 0 + bias) {
68 | /* number less than 1 */
69 | num = 0;
70 | } else if (exp >= 31 + bias) {
71 | /* number overflow */
72 | /* or f < 0 and (int)f == INT_MIN */
73 | num = 0x80000000;
74 | } else {
75 | E = exp - bias;
76 | M = frac | 0x800000;
77 | if (E > 23) {
78 | num = M << (E - 23);
79 | } else {
80 | /* whether sig is 1 or 0, round to zero */
81 | num = M >> (23 - E);
82 | }
83 | }
84 |
85 | return sig ? -num : num;
86 | }
--------------------------------------------------------------------------------
/第二章答案/2.97.c:
--------------------------------------------------------------------------------
1 | /*
2 | * float-i2f.c
3 | */
4 | #include
5 | #include
6 | #include
7 | #include "float-i2f.h"
8 |
9 | /*
10 | * Assume i > 0
11 | * calculate i's bit length
12 | *
13 | * e.g.
14 | * 0x3 => 2
15 | * 0xFF => 8
16 | * 0x80 => 8
17 | */
18 | int bits_length(int i) {
19 | if ((i & INT_MIN) != 0) {
20 | return 32;
21 | }
22 |
23 | unsigned u = (unsigned)i;
24 | int length = 0;
25 | while (u >= (1< 0x00000007
37 | * 16 => 0x0000FFFF
38 | */
39 | unsigned bits_mask(int l) {
40 | return (unsigned) -1 >> (32-l);
41 | }
42 |
43 | /*
44 | * Compute (float) i
45 | */
46 | float_bits float_i2f(int i) {
47 | unsigned sig, exp, frac, rest, exp_sig /* except sig */, round_part;
48 | unsigned bits, fbits;
49 | unsigned bias = 0x7F;
50 |
51 | if (i == 0) {
52 | sig = 0;
53 | exp = 0;
54 | frac = 0;
55 | return sig << 31 | exp << 23 | frac;
56 | }
57 | if (i == INT_MIN) {
58 | sig = 1;
59 | exp = bias + 31;
60 | frac = 0;
61 | return sig << 31 | exp << 23 | frac;
62 | }
63 |
64 | sig = 0;
65 | /* 2's complatation */
66 | if (i < 0) {
67 | sig = 1;
68 | i = -i;
69 | }
70 |
71 | bits = bits_length(i);
72 | fbits = bits - 1;
73 | exp = bias + fbits;
74 |
75 | rest = i & bits_mask(fbits);
76 | if (fbits <= 23) {
77 | frac = rest << (23 - fbits);
78 | exp_sig = exp << 23 | frac;
79 | } else {
80 | int offset = fbits - 23;
81 | int round_mid = 1 << (offset - 1);
82 |
83 | round_part = rest & bits_mask(offset);
84 | frac = rest >> offset;
85 | exp_sig = exp << 23 | frac;
86 |
87 | /* round to even */
88 | if (round_part < round_mid) {
89 | /* nothing */
90 | } else if (round_part > round_mid) {
91 | exp_sig += 1;
92 | } else {
93 | /* round_part == round_mid */
94 | if ((frac & 0x1) == 1) {
95 | /* round to even */
96 | exp_sig += 1;
97 | }
98 | }
99 | }
100 |
101 | return sig << 31 | exp_sig;
102 | }
--------------------------------------------------------------------------------