CSAPP 第二章 信息的表示与处理 教材习题
2.58
int is_little_endian()
{
unsigned int x = 1;
unsigned char *start = (unsigned char *)(&x);
return start[0];
}
2.59
(y >> 8 << 8) | (x & 0xFF)
2.60
unsigned replace_byte(unsigned x, int i, unsigned char b)
{
int k = i * 8;
return x ^ ((x >> k & 0xFF) << k) ^ (b << k);
}
2.61
A. !(~x)
B. !x
C. !(0xFF ^ (x & 0xFF))
D. !(0xFF ^ (x >> (w - 8) & 0xFF))
2.62
int shifts_are_arithmetic()
{
int w = (sizeof(int)) << 3;
int x = (-1) >> 1;
return x >> (w - 1) & 1;
}
2.63
unsigned srl(signed x, int k)
{
unsigned xsra = (int) x >> k;
int w = (sizeof(int)) << 3;
unsigned v = ~(-1 << (w - k));
return xsra & v; // 只保留 xsra 的最低的 w - k 位即可。
}
int sra(int x, int k)
{
int xsrl = (unsigned) x >> k;
int w = (sizeof(int)) << 3;
int sgn = (1 << (w - 1)) & x;
int v = -1 << (w - k);
v &= !sgn - 1; // 如果 x 为负,则 v 不变,如果 x 非负,则 v 变为 0。
return xsrl | v;
}
2.64
int any_odd_one(unsigned x)
{
unsigned v = 0x555555555;
return !(!(x & v));
}
2.65
int odd_ones(unsigned x)
{
x ^= x >> 1;
x ^= x >> 2;
x ^= x >> 4;
x ^= x >> 8;
x ^= x >> 16; // 此时得到的 x 的最低位是原来的 x 各个位置上的数字异或和的结果
return x & 1;
}
2.66
int leftmost_one(unsigned x)
{
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16; // 此时得到的 x 形如 000...000111...111 且最高位 1 的位置和原来相同
return x - (x >> 1);
}
2.67
A. 当左移位数大于等于机器字长时,此时进行的左移操作属于未定义行为,可能返回预期之外的结果。
B. 可以改为
int int_size_is_32() // works for w >= 32
{
int t = 1 << 31;
return t == -t;
}
C. 可以改为
int int_size_is_32() // works for w >= 16
{
int t = 1 << 15;
t <<= 15;
t <<= 1;
return t == -t;
}
2.68
int lower_one_mask(int n)
{
unsigned x = (unsigned)(-1);
int w = (sizeof(int)) << 3;
return (int)(x >> (w - n)); // 这里要用逻辑右移
}
2.69
unsigned rotate_left(unsigned x, int n)
{
int w = (sizeof(int)) << 3;
return (x << n) | (x >> (w - n));
}
2.70
int fits_bits(int x, int n)
{
// 判断 x 的前 w-n 位是否全为 0 或者全为 1 即可
unsigned int v = (~0u) >> (w - n);
int a = x & ((int)(v));
v = ~v;
int b = x | ((int)(v));
return x == a || x == b;
}
2.71
A. 这段代码将抽取出的字节作为一个无符号数返回,而不是有符号数。
B. 正确的实现:
int xbyte(packed_t word, int bytenum)
{
int x = (word >> (bytenum << 3)) & 0xFF;
x -= (x >> 7 & 1) << 8;
return x;
}
2.72
A. 因为 sizeof(val)
返回的是无符号数类型,计算 maxbytes-sizeof(val)
前会进行强制转换,进行的是无符号数减法,结果总是大于等于 0 的。
B. 将条件改为 if(maxbytes >= sizeof(val))
即可。
2.73
int saturating_add(int x, int y)
{
int w = (sizeof(int)) << 3;
int TMin = 1 << (w - 1);
int TMax = TMin - 1;
int sum = x + y;
int pos_overflow = !(x & TMin) && !(y & TMin) && (sum & TMin); // 用逻辑运算代替 if 语句
int neg_overflow = (x & TMin) && (y & Tmin) && !(sum & TMin);
pos_overflow && (sum = TMax); // 用短路特性代替 if 语句
neg_overflow && (sum = TMin);
return sum;
}
2.74
int sub_ok(int x, int y)
{
int sub = x - y;
int w = (sizeof(int)) << 3;
int TMin = 1 << (w - 1);
int pos_overflow = !(x & TMin) && (y & TMin) && (sub & TMin);
int neg_overflow = (x & TMin) && !(y & TMin) && !(sub & TMin);
return !(pos_overflow) && !(neg_overflow);
}
2.75
unsigned unsigned_high_prod(unsigned x, unsigned y)
{
int w = (sizeof(int)) << 3;
int v = signed_high_prod((int)(x), (int)(y));
unsigned result = (unsigned)(v);
result += (!(x >> (w - 1) & 1) - 1) & y;
result += (!(y >> (w - 1) & 1) - 1) & x;
return result;
}
2.76
void *calloc(size_t nmemb, size_t size)
{
if (!nmemb || !size)
return NULL;
size_t bufsiz = nmemb * size;
if (bufsiz / size == nmemb)
{
void *ptr = malloc(bufsiz);
if (ptr != NULL)
memset(ptr, 0, bufsiz);
return ptr;
}
return NULL;
}
2.77
A. (x << 4) + x
B. x - (x << 3)
C. (x << 6) - (x << 2)
D. (x << 4) - (x << 7)
2.78
int divide_power2(int x, int k)
{
int w = (sizeof(int)) << 3;
int bias = (1 << k) - 1;
int sgn = x >> (w - 1) & 1;
bias &= (!sgn) - 1;
return (w + bias) >> k;
}
2.79
int mul3div4(int x)
{
x = (x << 1) + x;
int bias = 3;
int w = (sizeof(int)) << 3;
bias &= !(x >> (w - 1) & 1) - 1;
return (x + bias) >> 2;
}
2.80
题目的意思应该是计算 \(\frac 3 4 x\) 的值并向零舍入。
int threefourths(int x) // 分成前 30 位和后 2 位分别计算
{
int w = (sizeof(int)) << 3;
int a = x ^ (x & 3);
int b = x & 3;
int result = a >> 2;
result += result << 1; // 前 30 位不存在舍入问题,先除再乘
b += b << 1;
b += (!(x >> (w - 1) & 1) - 1) & 3;
b >>= 2; // 后 2 位不存在溢出问题,先乘再除
result += b;
return result;
}
2.81
A. (-1) << k
B. (unsigned)((-1) << (k + j)) >> k
2.82
A. 当 x 和 y 其中恰好有一者为 TMin 时,表达式为 0。
B. 总为 1,因为带符号的加减法与乘法满足交换律与结合律。
C. 总为 1,因为反码 = 补码 + 1。
D. 总为 1,因为类型转换时二进制编码不变。
E. 总为 1,因为算术右移是向下取整的。
2.83
A. 记这个无穷串的值为 \(x\) ,则 \(x\times 2^k-Y=x\) ,解得 \(x=\frac{Y}{2^k-1}\) 。
B. \((a).\frac{5}{7}\ (b).\frac{6}{15}=\frac{2}{5}\ (c).\frac{19}{63}\)
2.84
int float_le(float x, float y)
{
unsigned ux = f2u(x);
unsigned uy = f2u(y);
unsigned sx = ux >> 31;
unsigned sy = uy >> 31;
return (sx == 1 && sy == 0) || // x 为负,y 为非负
(sx == 1 && sy == 1 && ux >= uy) || // 均为负
(sx == 0 && sy == 0 && ux <= uy) || // 均为非负
((ux << 1) == 0 && (uy << 1) == 0); // 均为 0
}
2.85
A. 阶码 \(E=2\) ,尾数 \(M=1.75\) ,小数 \(f=0.75\) ,值 \(V=7\) 。位表示为 \(0\ \underbrace{1000\dots01}_{k\ 位}\ \underbrace{1100\dots00}_{n\ 位}\) 。
B. 阶码 \(E=n\) ,尾数 \(M=2-2^{-n}\) ,小数 \(f=1-2^{-n}\) ,值 \(V=2^{n+1}-1\) ,位表示为 \(0\ \underbrace{(2^{k-1}-1+n)_{(2)}}_{k\ 位}\ \underbrace{1111\dots11}_{n\ 位}\) 。
C. 最小的规格化数的倒数为 \(2^{2^{k-1}-2}\) ,阶码 \(E=2^{k-1}-2\) ,尾数 \(M=1\) ,小数 \(f=0\) ,值 \(V=2^{k-1}-2\) ,位表示为 \(0\ \underbrace{1111\dots1101}_{k\ 位}\ \underbrace{00\dots00}_{n\ 位}\) 。
2.86
A. 最小的正非规格化数为 \(2^{2-2^{14}} \times 2^{-63}\) ,十进制值约为 \(3.6452\times 10^{-4951}\) 。
B. 最小的正规格化数为 \(2^{2-2^{14}}\) ,十进制值约为 \(3.3621\times 10^{-4932}\) 。
C. 最大的规格化数为 \(2^{2^{14}}\times (2-2^{-63})\) ,十进制值约为 \(2.3794\times 10^{4932}\) 。
2.87
2.88
2.89
A. 正确。因为从 int 转换到 double 时 不会溢出,也不会舍入。
B. 错误。例如当 x 为 0 , y 为 TMin 时,左侧结果为 -TMin ,右侧结果为 TMin 。
C. 正确。因为 dx,dy,dz 都是由 int 类型整数转换过来的,相加时能精确进行。
D. 错误。当 dx,dy,dz 较大时,其中两者相乘可能会超出 \(2^{53}\) ,导致精度丢失。
E. 错误。当 dx,dz 其中存在 0 时,等式的一端会是 NaN,而任何数(包括 NaN 自己)与 NaN 做相等判断返回值都是 0。
2.90
float fpwr2(int x)
{
unsigned exp, frac;
unsigned u;
if (x < -149) // 1 - 127 - 23 = -149
{
// Too small. Return 0.0
exp = 0;
frac = 0;
}
else if (x < -126) // 1 - 127
{
// Denormalized result.
exp = 0;
frac = 1 << (23 + (x + 126));
}
else if (x < 128) // 254 - 127 + 1
{
// Normalized result.
exp = x + 127;
frac = 0;
}
else
{
// Too big. Return +oo
exp = 255;
frac = 0;
}
// Pack exp and frac into 32 bits
u = exp << 23 | frac;
// Return as float
return u2f(u);
}
2.91
A. 这个浮点数的二进制表示为 \(0\ 10000000\ 10010010000111111011011\) ,表示的二进制小数为 \(11.0010010000111111011011_{(2)}\) 。
B. \(\frac{22}{7}\) 的二进制小数表示为 \(11.001\ 001\ 001\ 001\dots\) 。
C. 对比可以发现这两个近似值是从二进制小数点后第九位开始不同的。
2.92
typedef unsigned float_bits;
float_bits float_negate(float_bits f)
{
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if (exp == 255 && frac != 0) // f 为 NaN
return f;
return f ^ (1 << 31);
}
2.93
typedef unsigned float_bits;
float_bits float_absval(float_bits f)
{
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if (exp == 255 && frac != 0) // f 为 NaN
return f;
return f & 0x7FFFFFFF;
}
2.94
typedef unsigned float_bits;
float_bits float_twice(float_bits f)
{
unsigned sign = f >> 31;
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if (exp == 255) // f 为 NaN 或 inf
return f;
if (exp == 0) // 非规格化数
frac <<= 1;
else // 规格化数
{
exp += 1;
if (exp == 255) // 溢出变为 inf
frac = 0;
}
return (sign << 31) | (exp << 23) | frac;
}
这里当 f 为非规格化数时,直接将 frac 左移一位,溢出部分会自动放到 exp 中,即使 f 乘 2 后变为了规格化数,结果同样是正确的。
这种情况下原来在 frac 部分首位的 1 被左移到 exp 部分内,使得 exp 变为 1,f 变为规格化数,但 E 仍为 1 - 127 ,而在计算尾数时,由于 f 已经变为了规格化数,这个 1 又会被加上去,最后计算出来的结果是正确的。
2.95
typedef unsigned float_bits;
float_bits float_half(float_bits f)
{
unsigned sign = f >> 31;
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
int val = (frac & 3) == 3; // 向偶数舍入,只有当末两位都是 1 时,右移后需要加上 1
if (exp == 255) // f 为 NaN 或 inf
return f;
if (exp == 0) // 非规格化数
{
frac >>= 1;
frac += val;
}
else // 规格化数
{
exp -= 1;
if (exp == 0) // 规格化数变为非规格化数
{
frac += 1 << 23; // 补上尾数额外加的 1
frac >>= 1;
frac += val; // 溢出部分会自动放到 exp 中
}
}
return (sign << 31) | (exp << 23) | frac;
}
这里当规格化数变为非规格化数时,由于采用向偶数舍入,对 frac +1 时可能导致其溢出,重新变为规格化数,这里的处理方式同上题一样,将溢出的 1 自动放到 exp 部分中即可。
2.96
typedef unsigned float_bits;
int float_f2i(float_bits f)
{
unsigned sign = f >> 31;
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if (exp == 255) // f 为 NaN 或 inf
return 0x80000000;
else if (exp == 0) // 非规格化数
return 0;
else if (exp - 127 < 0) // 绝对值小于 1
return 0;
else if (sign == 0 && exp - 127 > 30) // 超出 TMax
return 0x80000000;
else if (sign == 1 && (exp - 127 > 31 || (exp - 127 == 31 && frac != 0))) // 超出 TMin
return 0x80000000;
else
{
int k = exp - 127;
if (sign == 1 && k == 31 && frac == 0)
return (1 << 31);
frac += 1 << 23;
if (k >= 23)
frac <<= (k - 23);
else if (k < 23)
frac >>= (23 - k);
if (sign)
frac = -frac;
}
return frac;
}
2.97
typedef unsigned float_bits;
float_bits float_i2f(int i)
{
if (i == 0)
return 0;
unsigned sign = 0;
if (i < 0)
{
sign = 1;
i = -i;
}
int k = 31;
while (k >= 0)
{
if (i >> k & 1)
break;
k -= 1;
}
unsigned exp = k + 127;
unsigned frac = i & ((1 << k) - 1);
if (k <= 23)
frac <<= (23 - k);
else
{
int val = frac & ((1 << (k - 23)) - 1);
int add = 0;
if (val > (1 << (k - 24)))
add = 1;
else if (val == (1 << (k - 24)) && (frac >> (k - 23) & 1))
add = 1;
frac >>= (k - 23);
frac += add;
}
return (sign << 31) | (exp << 23) | frac;
}