• 沒有找到結果。

它们利用不同移位运算的零填充和符号扩展的属性。请注意强制类型转换和移位运算的顺序。在fun1 中,

移位是在无符号word 上进行的,因此是逻辑移位。在 fun2 中,移位是在把 word 强制类型转换为 int 之后进行的,因此是算术移位。

Solution to Problem 2.22 (page 108)

This exercise provides a concrete demonstration of how sign extension preserves the numeric value of a two’s-complement representation.

A. [1011]: −23+ 21+ 20 = −8 + 2 + 1 = −5

B. [11011]: −24+ 23+ 21+ 20 = −16 + 8 + 2 + 1 = −5

C. [111011]: −25+ 24+ 23+ 21+ 20 = −32 + 16 + 8 + 2 + 1 = −5 Solution to Problem 2.23 (page 108)

The expressions in these functions are common program “idioms” for extracting values from a word in which multiple bit fields have been packed. They exploit the zero-filling and sign-extending properties of the different shift operations.

Note carefully the ordering of the cast and shift operations. In fun1, the shifts are performed on unsigned variable word, and hence are logical. In fun2, shifts are performed after casting word to int, and hence are arithmetic.

A. w fun1(w) fun2(w)

0x00000076 0x00000076 0x00000076 0x87654321 0x00000021 0x00000021 0x000000C9 0x000000C9 0xFFFFFFC9 0xEDCBA987 0x00000087 0xFFFFFF87

B. Function fun1 extracts a value from the low-order 8 bits of the argument, giving an integer ranging between 0 and 255. Function fun2 extracts a value from the low-order 8 bits of the argument, but it also performs sign extension.

The result will be a number between −128 and 127.

Solution to Problem 2.24 (page 110)

The effect of truncation is fairly intuitive for unsigned numbers, but not for two’s-complement numbers. This exercise lets you explore its properties using very small word sizes.

Hex Unsigned Two’s complement

Original Truncated Original Truncated Original Truncated

0 0 0 0 0 0

2 2 2 2 2 2

9 1 9 1 −7 1

B 3 11 3 −5 3

F 7 15 7 −1 −1

As Equation 2.9 states, the effect of this truncation on unsigned values is to simply find their residue, modulo 8. The effect of the truncation on signed values is a bit more complex. According to Equation 2.10, we first compute the modulo 8 residue of the argument. This will give values 0 through 7 for arguments 0 through 7, and also for arguments −8 through −1. Then we apply function U2T3to these residues, giving two repetitions of the sequences 0 through 3 and −4 through −1.

B. 函数fun1 从参数的低 8 位中提取一个值,得到范围 0 ~ 255 之间的一个整数。函数 fun2 也从这 个参数的低8 位中提取一个值,但是它还要执行符号扩展。结果将是介于 -128 ~ 127 之间的一个数。

练习题2.24 对于无符号数来说,截断的影响是相当直观的,但是对于补码数却不是。这个练习让你使用

非常小的字长来研究它的属性。

正如等式(2-9)所描述的,这种截断无符号数值的结果就是发现它们模 8 余数。截断有符号数的结

果要更复杂一些。根据等式(2-10),我们首先计算这个参数模 8 后的余数。对于参数 0 ~ 7,将得出值 0 ~ 7,对于参数 -8 ~ -1 也是一样。然后我们对这些余数应用函数 U2T3,得出两个0 ~ 3 和 -4 ~ 1 序 列的反复。

练习题2.25 设计这个问题是要说明从有符号数到无符号数的隐式强制类型转换很容易引起错误。将参

数length 作为一个无符号数来传递看上去是件相当自然的事情,因为没有人会想到使用一个长度为

负数的值。停止条件i<=length-1 看上去也很自然。但是把这两点组合到一起,将产生意想不到的 结果!

因为参数length 是无符号的,计算 0-1 将进行无符号运算,这等价于模数加法。结果得到 UMax。

≤比较进行同样使用无符号数比较,而因为任何数都是小于或者等于 UMax 的,所以这个比较总是为真!

因此,代码将试图访问数组a 的非法元素。

有两种方法可以改正这段代码,其一是将length 声明为 int 类型,其二是将 for 循环的测试条件改 为i < length。

练习题2.26 这个例子说明了无符号运算的一个细微的特性,同时也是我们执行无符号运算时不会意识到

的属性。这会导致一些非常棘手的错误。

A. 在什么情况下,这个函数会产生不正确的结果?当s 比 t 短的时候,该函数会不正确地返回 1。

B. 解释为什么会出现这样不正确的结果。由于strlen 被定义为产生一个无符号的结果,差和比较都

采用无符号运算来计算。当s 比 t 短的时候,差 strlen(s)-strlen(t) 会为负,但是变成了一个很大 的无符号数,且大于0。

C. 说明如何修改这段代码好让它能可靠地工作。将测试语句改成 : 176 Chapter 2 Representing and Manipulating Information

Solution to Problem 2.25 (page 111)

This problem is designed to demonstrate how easily bugs can arise due to the implicit casting from signed to unsigned. It seems quite natural to pass parameter lengthas an unsigned, since one would never want to use a negative length. The stopping criterion i <= length-1 also seems quite natural. But combining these two yields an unexpected outcome!

Since parameter length is unsigned, the computation 0 − 1is performed using unsigned arithmetic, which is equivalent to modular addition. The result is then UMax. The ≤ comparison is also performed using an unsigned comparison, and since any number is less than or equal to UMax, the comparison always holds!

Thus, the code attempts to access invalid elements of array a.

The code can be fixed either by declaring length to be an int, or by changing the test of the for loop to be i < length.

Solution to Problem 2.26 (page 111)

This example demonstrates a subtle feature of unsigned arithmetic, and also the property that we sometimes perform unsigned arithmetic without realizing it. This can lead to very tricky bugs.

A. For what cases will this function produce an incorrect result? The function will incorrectly return 1 when s is shorter than t.

B. Explain how this incorrect result comes about. Since strlen is defined to yield an unsigned result, the difference and the comparison are both com-puted using unsigned arithmetic. When s is shorter than t, the difference strlen(s) - strlen(t)should be negative, but instead becomes a large, unsigned number, which is greater than 0.

C. Show how to fix the code so that it will work reliably. Replace the test with the following:

return strlen(s) > strlen(t);

Solution to Problem 2.27 (page 115)

This function is a direct implementation of the rules given to determine whether or not an unsigned addition overflows.

/* Determine whether arguments can be added without overflow */

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

return sum >= x;

}

Solution to Problem 2.28 (page 116)

练习题2.27 这个函数是对确定无符号加法是否溢出的规则的直接实现。

正文.indd 95 2010-10-19 14:19:10

 96  第一部分 程序结构和执行 

Solution to Problem 2.25 (page 111)

This problem is designed to demonstrate how easily bugs can arise due to the implicit casting from signed to unsigned. It seems quite natural to pass parameter lengthas an unsigned, since one would never want to use a negative length. The stopping criterion i <= length-1 also seems quite natural. But combining these two yields an unexpected outcome!

Since parameter length is unsigned, the computation 0 − 1is performed using unsigned arithmetic, which is equivalent to modular addition. The result is then UMax. The ≤ comparison is also performed using an unsigned comparison, and since any number is less than or equal to UMax, the comparison always holds!

Thus, the code attempts to access invalid elements of array a.

The code can be fixed either by declaring length to be an int, or by changing the test of the for loop to be i < length.

Solution to Problem 2.26 (page 111)

This example demonstrates a subtle feature of unsigned arithmetic, and also the property that we sometimes perform unsigned arithmetic without realizing it. This can lead to very tricky bugs.

A. For what cases will this function produce an incorrect result? The function will incorrectly return 1 when s is shorter than t.

B. Explain how this incorrect result comes about. Since strlen is defined to yield an unsigned result, the difference and the comparison are both com-puted using unsigned arithmetic. When s is shorter than t, the difference strlen(s) - strlen(t)should be negative, but instead becomes a large, unsigned number, which is greater than 0.

C. Show how to fix the code so that it will work reliably. Replace the test with the following:

return strlen(s) > strlen(t);

Solution to Problem 2.27 (page 115)

This function is a direct implementation of the rules given to determine whether or not an unsigned addition overflows.

/* Determine whether arguments can be added without overflow */

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

return sum >= x;

}

Solution to Problem 2.28 (page 116)

This problem is a simple demonstration of arithmetic modulo 16. The easiest way to solve it is to convert the hex pattern into its unsigned decimal value. For nonzero values of x, we must have (-u4x)+ x = 16. Then we convert the complemented value back to hex.

练习题2.28 本题是对算术模 16 的简单示范。最容易的解决方法是将十六进制模式转换成它的无符号十

进制值。对于非零的x 值,一定有 (

Solution to Problem 2.25 (page 111)

This problem is designed to demonstrate how easily bugs can arise due to the implicit casting from signed to unsigned. It seems quite natural to pass parameter lengthas an unsigned, since one would never want to use a negative length. The stopping criterion i <= length-1 also seems quite natural. But combining these two yields an unexpected outcome!

Since parameter length is unsigned, the computation 0 − 1is performed using unsigned arithmetic, which is equivalent to modular addition. The result is then UMax. The ≤ comparison is also performed using an unsigned comparison, and since any number is less than or equal to UMax, the comparison always holds!

Thus, the code attempts to access invalid elements of array a.

The code can be fixed either by declaring length to be an int, or by changing the test of the for loop to be i < length.

Solution to Problem 2.26 (page 111)

This example demonstrates a subtle feature of unsigned arithmetic, and also the property that we sometimes perform unsigned arithmetic without realizing it. This can lead to very tricky bugs.

A. For what cases will this function produce an incorrect result? The function will incorrectly return 1 when s is shorter than t.

B. Explain how this incorrect result comes about. Since strlen is defined to yield an unsigned result, the difference and the comparison are both com-puted using unsigned arithmetic. When s is shorter than t, the difference strlen(s) - strlen(t)should be negative, but instead becomes a large, unsigned number, which is greater than 0.

C. Show how to fix the code so that it will work reliably. Replace the test with the following:

return strlen(s) > strlen(t);

Solution to Problem 2.27 (page 115)

This function is a direct implementation of the rules given to determine whether or not an unsigned addition overflows.

/* Determine whether arguments can be added without overflow */

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

return sum >= x;

}

Solution to Problem 2.28 (page 116)

This problem is a simple demonstration of arithmetic modulo 16. The easiest way to solve it is to convert the hex pattern into its unsigned decimal value. For nonzero values of x, we must have (-u4x)+ x = 16. Then we convert the complemented

value back to hex. x) + x = 16。然后,我们就可以将取补后的值转换回十六进制。

练习题2.29 本题的目的是确定你理解了补码加法。

练习题2.30 这个函数是对确定补码加法是否溢出的规则的直接实现。

Solutions to Practice Problems 177

x -u4x

Solution to Problem 2.29 (page 120)

This problem is an exercise to make sure you understand two’s-complement addition.

Solution to Problem 2.30 (page 120)

This function is a direct implementation of the rules given to determine whether or not a two’s-complement addition overflows.

/* Determine whether arguments can be added without overflow */

int tadd_ok(int x, int y) { int sum = x+y;

int neg_over = x < 0 && y < 0 && sum >= 0;

int pos_over = x >= 0 && y >= 0 && sum < 0;

return !neg_over && !pos_over;

}

Solution to Problem 2.31 (page 120)

Your coworker could have learned, by studying Section 2.3.2, that two’s-complement addition forms an abelian group, and so the expression (x+y)-x will evaluate to y regardless of whether or not the addition overflows, and that (x+y)-ywill always evaluate to x.

Solution to Problem 2.32 (page 121)

This function will give correct values, except when y is TMin. In this case, we will have -y also equal to TMin, and so function tadd_ok will consider there to be

练习题2.31 通过对 2.3.2 节的学习,你的同事可能已经学会补码加上形成一个阿贝尔群,以及表达式

对于无符号数的非,位的模式是相同的。

练习题2.34 本题的目的是确定你理解了补码乘法。

练习题2.35 用所有可能的x 和 y 测试一遍这个函数显然是不现实的。当数据类型 int 为 32 位时,即

使你每秒运行一百亿个测试,也需要58 年才能完成所有的组合。另一方面,把函数中的数据类型改成

short 或者 char,然后再穷尽测试,倒是测试代码的一种可行的方法。

我们提出以下论据,这是一个更理论的方法 :

1. 我们知道 x · y 可以写成一个 2w 位的补码数字。用 u 表示低 w 位的无符号数,v 表示高 w 位的补码 数字。那么,根据等式(2-3),我们可以得到 x · y = v2w+u。

我们还知道u = T2Uw( p ),因为它们是从同一个位模式得出来的无符号和补码数字,因此根据等式

2-5),我们有 u = p + pw-12w,这里pw-1p 的最高有效位。设 t = v + pw-1,我们得到x · y = p + t2w。 当t = 0 时,有 x · y = p ;乘法不会溢出。当 t ≠ 0 时,有 x · y ≠ p ;乘法溢出。

2. 根据整数除法的定义,用非零数 x 除以 p 会得到商 q 和余数 r,即 p = x · q + r,且 | r | < | x |。(这里 用的是绝对值,因为x 和 r 的符号可能不一致。例如,-7 除以 2 得到商 -3 和余数 -1。)

3. 假设 q = y,那么有 x · y = x · y + r + t2w。在此,我们可以得到r + t2w = 0。但是 | r | < | x | ≤ 2w,所以 只有当t = 0 时,这个等式才会成立,此时 r = 0。

假设r = t = 0,那么我们有 x · y = x · q,隐含有 y = q。

x = 0 时,乘法不溢出,所以我们的代码提供了一种可靠的方法来测试补码乘法是否会导致溢出。

练习题2.36 如果用 64 位表示,乘法就不会有溢出。然后我们来验证将乘积强制类型转换为 32 位是否会 改变它的值 :

Solutions to Practice Problems 179 We also know that u = T2Uw(p), since they are unsigned and

two’s-complement numbers arising from the same bit pattern, and so by Equa-tion 2.5, we can write u = p + pw−12w, where pw−1is the most significant bit of p. Letting t = v + pw−1, we have x . y = p + t2w.

When t = 0, we have x . y = p; the multiplication does not overflow. When t�= 0, we have x . y �= p; the multiplication does overflow.

2. By definition of integer division, dividing p by nonzero x gives a quotient qand a remainder r such that p = x . q + r, and |r| < |x|. (We use absolute values here, because the signs of x and r may differ. For example, dividing −7 by 2 gives quotient −3 and remainder −1.)

3. Suppose q = y. Then we have x . y = x . y + r + t2w. From this, we can see that r + t2w= 0. But |r| < |x| ≤ 2w, and so this identity can hold only if t = 0, in which case r = 0.

Suppose r = t = 0. Then we will have x . y = x . q, implying that y = q.

When x equals 0, multiplication does not overflow, and so we see that our code provides a reliable way to test whether or not two’s-complement multiplication causes overflow.

Solution to Problem 2.36 (page 125)

With 64 bits, we can perform the multiplication without overflowing. We then test whether casting the product to 32 bits changes the value:

1 /* Determine whether arguments can be multiplied without overflow */

2 int tmult_ok(int x, int y) {

3 /* Compute product without overflow */

4 long long pll = (long long) x*y;

5 /* See if casting to int preserves value */

6 return pll == (int) pll;

7 }

Note that the casting on the right-hand side of line 4 is critical. If we instead

Note that the casting on the right-hand side of line 4 is critical. If we instead

相關文件