把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

\[\begin{array}{|c||c|c|c|c|c|c|} \hline 描述 & \rm Hex & \textit M & \textit E & \textit V & \textit D \\ \hline -0 & \rm 0x8000 & 0 & -14 & -0 & -0.0 \\ \hline 最小的 >2\ 的值 & \rm 0x4001 & 1+2^{-10} & 1 & (2^9+1)\times 2^{-9} & 2.001953125 \\ \hline 512 & \rm 0x6000 & 1 & 9 & 1\times 2^9 & 512.0\\ \hline 最大的非规格化数 & \rm 0x03FF & 1-2^{-10} & -14 & (2^{10}-1)\times 2^{-24} & 6.09755516\times 10^{-5}\\ \hline -\infty & \rm 0x1C00 & - & - & -\infty & -\infty\\ \hline \rm 0x3BB0 & \rm0x3BB0 & 123\times 2^{-6} & -1 & 123 \times 2^{-7} & 0.9609375\\ \hline \end{array} \]


2.88

\[\begin{array}{|c|c|c|c|} \hline \rm 格式\ A\ 的 位 & \rm 格式\ A\ 的 值 & \rm 格式\ B\ 的 位 & \rm 格式\ B\ 的 值\\ \hline 1\ 01110\ 001 & -9\times 2^{-4} & 1\ 0110\ 0010 & -9\times 2^{-4}\\ \hline 0\ 10110\ 101 & 13\times 2^4 & 0\ 1110\ 1010 & 13\times 2^4\\ \hline 1\ 00111\ 110 & -7\times 2^{-10} & 1\ 0000 \ 0111 &-7\times 2^{-10}\\ \hline 0\ 00000\ 101 & 5\times 2^{-17} & 0\ 0000\ 0001\ & 2^{-10}\\ \hline 1\ 11011\ 000 &-2^{12} & 1\ 1110\ 1111 & -31\times 2^3\\ \hline 0\ 11000 \ 100 & 3\times 2^8 & 0\ 1111\ 0000 & +\infty\\ \hline \end{array} \]


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;
}
posted @ 2023-01-06 20:20  jklover  阅读(136)  评论(0编辑  收藏  举报