深入理解计算机系统 csapp 家庭作业(第二章完整版)

第二章

2.55

代码示例:`

#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
    size_t i;
    for (i = 0; i < len; i++)
    printf("%.2x", start[i]); 
    printf("\n");
}

void show_int(int x){
    show_bytes((byte_pointer) &x, sizeof(int));
}

void show_float(float x){
    show_bytes((byte_pointer) &x, sizeof(float));
}

void show_pointer(void *x){
    show_bytes((byte_pointer) &x, sizeof(void *));
}

void test_show_bytes(int val){
    int ival=val;
    float fval=(float)ival;
    int *pval=&ival;
    show_int(ival);
    show_float(fval);
    show_pointer(pval);
}

int main(){
    int val=12345;
    test_show_bytes(val);
    return 0;
}`

运行结果:
39300000
00e44046
f4fe20effe7f0000
使用的机器采用小端法存储

2.56

同上

2.57

#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
    size_t i;
    for (i = 0; i < len; i++)
    printf("%.2x", start[i]); 
    printf("\n");
}

void show_short(short x){
    show_bytes((byte_pointer) &x, sizeof(short));
}

void show_long(long x){
    show_bytes((byte_pointer) &x, sizeof(long));
}

void show_double(double x){
    show_bytes((byte_pointer) &x, sizeof(double));
}

void test_show_bytes(int val){
    short sval=(short)val;
    long lval=(long)val;
    double dval=(double)val;
    show_short(sval);
    show_long(lval);
    show_double(dval);
}

int main(){
    int val=12345;
    test_show_bytes(val);
    return 0;
}

运行结果:
3930
3930000000000000
00000000801cc840

2.58

由2.55可知,12345的16位小端法表示为39300000,因此判断数据存储为大端法还是小端法,只需要判断第一个字节是否为39即可

#include <stdio.h>

typedef unsigned char *byte_pointer;

int is_little_endian(){
    int test=12345;
    byte_pointer p=(byte_pointer)&test;
    return p[0]==0x39;
}

int main(){
    if(is_little_endian())
    printf("the machine is little endian\n");
    else
    printf("the machine is big endian\n");
}

2.59

使用位运算和掩码结合字节

#include <stdio.h>

typedef unsigned char *byte_pointer;

size_t combine(size_t x,size_t y){
    size_t mask=0xff;
    return ((x&mask)|(y&~mask));
}

int main(){
    size_t x=0x89abcdef,y=0x765432ef;
    printf("%#x\n",combine(x,y));
}

2.60

#include <stdio.h>

typedef unsigned char* byte_pointer;
size_t replace_byte(unsigned x,int i,unsigned char b){
    size_t mask=((unsigned)0xff)<<(i<<3);
    return (x&(~mask))|(((unsigned)b)<<(i<<3));
}

int main(){
    printf("%x\n%x\n",replace_byte(0x12345678,0,0xAB),replace_byte(0x12345678,3,0xAB));
}

2.61

A.!~x
B.!x
C.!~(x|~0xff) //x为0xff,~(x|~0xff)=0x0
D.!(x>>((sizeof(int)-1) << 3)) 
//(sizeof(int)-1是移动三字节,保留最高位,<<3是*2^3,移动8位(1个字节)

2.62

算数右移会补充符号位,而逻辑右移不会,因此用-1进行测试,算数右移后-1为0x11111111,与原来相等,而逻辑右移后-1变为0x1FFFFFFF,原来不相等。

#include<stdio.h>

int int_shifts_are_arithmetic(){
    int x=-1;
    return x==x>>3;
}

int main(){  
    printf("%d\n",int_shifts_are_arithmetic());
}

2.63

#include <stdio.h>

unsigned srl(unsigned x,int k){
    unsigned xsra = (int) x>>k;
    int w =sizeof(int)<<3; //w是int的位数,即题干中说的8*sizeof(int)
    int mask = (int)-1<<(w-k); //掩码为0xc0000000
    return xsra&(~mask);  //符号清零
}

int sra(int x,int k){
    int xsrl = (unsigned) x>>k;
    int w = sizeof(int)<<3;
    int mask = (int)-1<<(w-k);
    int mmask = (int)-1<<(w-1); //mmask用来取x的符号位
    mask &= (!(mmask&x)-1); //若补1,则mask为0xc0000000;若补0,则mask为0
    return xsrl|mask; //填加符号
}

int main(){
    unsigned testu = 0x12345678;
    int testi = 0x12345678;
    printf("%x\n",srl(testu,2));
    printf("%x\n",sra(testi,2));
}

2.64

当 x 的任何奇数位等于 1 时返回 1;否则为 0。

#include <stdio.h>

int any_odd_one(unsigned x){
    return !!(0xAAAAAAAA&x);
}

int main(){
    int a = 0x0111;
    printf("%d",any_odd_one(a));
}

2.65

当 x 包含奇数个1 时,返回 1;否则为 0。

#include <stdio.h>
int odd_ones(unsigned x){
    x^=x>>16;
    x^=x>>8;
    x^=x>>4;
    x^=x>>2;
    x^=x>>1;
    return x&1;
}
int main(){
    int a = 0x0101;
    printf("%d",odd_ones(a));
}

2.66

#include <stdio.h>

int leftmost_one(unsigned x) {
  x |= x >> 1;
  x |= x >> 2;
  x |= x >> 4;
  x |= x >> 8;
  x |= x >> 16;
  return (x >> 1) + (x && 1);	//(x && 1)用来解决x=0x时的情况
}

int main() {
  printf("%x\n",leftmost_one(0x6600));
  return 0;
}

2.67

A.32位机器,最多只能移动31位,这个代码超出了机器能移动的位数,超出位无定义。
B.

#include<stdio.h>

int int_size_is_32()
{
	int set_msb=1<<31;
	int beyond_msb=set_msb<<1; //将刚刚移动的31位再左移一位
	return set_msb && !beyond_msb;
}

int main(){
    printf("%d",int_size_is_32());
}

C.

#include<stdio.h>

int int_size_is_16() {
	int set_msb=1<<15;
	set_msb<<=15;
	set_msb<<=1;
	int beyond_msb=set_msb<<1;
	return set_msb && !beyond_msb;
}

int main(){
    printf("%d",int_size_is_16());
}

2.68

#include<stdio.h>

int lower_one_mask(int n) {
    int w = sizeof(int) << 3;
	return (unsigned)-1>>(w-n);
}

int main(){
    printf("%#x",lower_one_mask(17));
}

2.69

#include<stdio.h>

unsigned rotate_left(unsigned x, int n) {
    unsigned w1=x<<n;
    unsigned w2=x>>((sizeof(int)<<3)-n);
	return w1|w2;
}

int main(){
    printf("%#x",rotate_left(0x12345678, 20));
}

2.70

如果x可以用N位的2进制补码表示,则返回1

#include <stdio.h>
#include <limits.h>

int fits_bits(int x,int n){
    int w=sizeof(int)<<3;
    return x == x<<(w-n)>>(w-n);
}

int main(){
    printf("%d\n",fits_bits(INT_MAX, 31));
    printf("%d\n",fits_bits(INT_MAX, 32));
}

2.71

A.这个函数不能提取负值,因为word是unsigned类型,会进行无符号扩展,不能得到题目中要求的int类型。
B.

#include <stdio.h>

typedef unsigned packed_t;

int xbyte(packed_t word,int bytenum){
    return ((int)(word<<((3-bytenum)<<3)))>>24;
}
 
int main(){
    printf("%d\n",xbyte(-2,0));
}

2.72

A.因为size_t的返回值类型是unsigned,所以相减结果的返回值也是unsigned,大于等于0恒成立
B.

#include <stdio.h>

void copy_int(int val,void *buf,unsigned maxbytes){
    if(maxbytes>=sizeof(val))  
    //将maxbytes也改为unsigned类型,比较两个unsigned值的大小
        memcpy(buf,(void*)&val,sizeof(val));
}

int main(){
    int max = 100;
    void *buff = malloc(max);
    copy_int(100,&buff,200);
}

2.73

#include <stdio.h>
#include <limits.h>
int saturating_add(int x, int y) {
  int sum=x+y;
  int sig_mask=INT_MIN;
  
  int pos_over=!(x & sig_mask) && !(y & sig_mask) && (sum & sig_mask);//x>0,y>0,正溢出
  int neg_over=(x & sig_mask) && (y & sig_mask) && !(sum & sig_mask);//x<0,y<0,负溢出
  
  pos_over && (sum = INT_MAX) || neg_over && (sum = INT_MIN);
  return sum;
}
int main(){
    printf("%#x",saturating_add(INT_MIN,-1));
}

2.74

#include <stdio.h>
#include <limits.h>
int tsub_ok(int x, int y)
{
    int res = 1;
    (y == INT_MIN) && (res = 0);// 如果y == INT_MIN,则res = 0(一定溢出);
    int sub = x - y;
    int pos_over = x > 0 && y < 0 && sub < 0;
    int neg_over = x < 0 && y > 0 && sub > 0;
    return res && !(pos_over || neg_over);
}

int main(){
    printf("%d",tsub_ok(INT_MIN,1));
}

2.75

由书上公式(2.18),x’代表无符号数x对应的补码

(x’ * y’) mod 2 w 2^w 2w =[ x ∗ y x * y xy + ( x w − 1 x_{w-1} xw1 y y y + y w − 1 y_{w-1} yw1 x x x ) 2 w 2^w 2w + x w − 1 x_{w-1} xw1 y w − 1 y_{w-1} yw1 2 w 2^w 2w ] mod 2 w 2^w 2w

因为 2 2 w 2^{2w} 22w 2 w 2^w 2w后仍超过w位被丢弃,故最后结果为
(x’ * y’) / 2 w 2^w 2w = ( x ∗ y x * y xy) / 2 w 2^w 2w+ ( x w − 1 x_{w-1} xw1 y y y + y w − 1 y_{w-1} yw1 x x x )

#include <stdio.h>
#include <inttypes.h>

int signed_high_prod(int x, int y) {
	int64_t high_prod = (int64_t)x * y;
	return high_prod >> 32;		//计算(x * y) / 2^w
}

unsigned unsigned_high_prod(unsigned x, unsigned y) {
	int bit_x = x>>31;		//bit_x表示 x_{w-1}
	int bit_y = y>>31;		//bit_y表示 y_{w-1}
	int sig_high = signed_high_prod(x, y);
	return sig_high + x * bit_y + y * bit_x;	//计算(x' * y') / 2^w
}

unsigned test(unsigned x, unsigned y) {		//验证结果是否正确
	uint64_t high_prod = (uint64_t)x * y;	
	return high_prod >> 32;
}

int main() {
	unsigned x = 0xffffffff;
	unsigned y = 0x12345678;
	printf("%.8x\n", unsigned_high_prod(x, y)); 
	printf("%.8x\n", test(x, y));
	return 0;
}

2.76

(本题用了if和乘除法,没有想到怎么用位运算实现)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
void *calloc(size_t nmemb, size_t size) {
    if (!(nmemb & size)) {
        return NULL;
    }
    size_t buf_size = nmemb * size;
  
 	if (nmemb == buf_size / size) {
    	void* ptr = malloc(buf_size);
    	if(ptr != NULL) {
      		memset(ptr, 0, buf_size);
    }
    return ptr;
  }
    return NULL;
    
}
int main(){
    printf("%d\n", calloc(INT_MAX, INT_MAX));
}

2.77

A.(x<<4)+x;

B.x-(x<<3);

C.(x<<6)-(x<<2);

D.(x<<4)-(x<<7);

2.78

C语言除法的向零舍入,即 x<0 且 x 的最后 k 位不为零时要加一

#include <stdio.h>
int divide_power2(int x, int k) {
    return (x+((1<<k)-1)*(x<0))>>k;  //(1<<k)-1是除法中的偏置量2^k-1
}
int main(){
    printf("%d\n",divide_power2(-9,2));
}

2.79

#include <stdio.h>
int mul3div4(int x){
    x = (x<<1)+x;
    return (x+((1<<2)-1)*(x<0))>>2;  //鉴于上一道题的思路
}
int main(){
    printf("%d\n",mul3div4(-5));
}

2.80

#include <stdio.h>
int threefourths(int x){
    x=(x+((1<<2)-1)*(x<0))>>2;   //思路同上
    return x+(x<<1);  
}
int main(){
    printf("%d\n",threefourths(-5));
}

2.81

A.-1<<k;
B.~(-1<<k)<<j

2.82

A. 错误,当x是INT_MAX时,-x也为负数
B. 正确
C. 错误,左侧 ~x+ ~y+1=-x-1-y-1;右侧 ~(x+y)=-x-y-1。左右侧不等,参数始终为0
D. 正确
E. 正确

2.83

A.Y/(2^k-1)
本题是一个等比数列求和,首项a1=Y/2 ^ k, 公比q=1/2 ^ k
B.(a)5/7 (b)2/5 (c)19/63

2.84

#include <stdio.h>
unsigned f2u(float x){
    return *(unsigned*)&x;
}
int float_le(float x,float y){
    unsigned ux = f2u(x);
    unsigned uy = f2u(y);
    unsigned sx = ux>>31;
    unsigned sy = uy>>31;
    return!(ux<<1 || uy<<1) ||        /* both zero         */
        (sx > sy) ||                  /* x < 0 and y >= 0  */
        (!sx && !sy && ux <= uy) ||   /* x >= 0 and y >= 0 */
        (sx && sx && ux >= uy);       /* x < 0 and y < 0   */
}
int main(){
    printf("%d\n",float_le(-0,+0));
    printf("%d\n",float_le(+0,-0));
}

2.85

注:C最小的规格化数是0 00…01 00…00,其值为 2 1 − ( 2 ( k − 1 ) − 1 ) 2^{1- (2^{(k-1)}-1)} 21(2(k1)1)= 2 2 − 2 k − 1 2^{2-2^{k-1}} 222k1
倒数为 2 2 k − 1 − 2 2^{2^{k-1}-2} 22k12,位表示为0 11…101 00000…

ABC
阶码2n 2 k − 1 − 2 2^{k-1}-2 2k12
尾数1.111.11…11.0
小数0.110.11…10.0
7.0 2 n + 1 2^{n+1} 2n+1-1 2 2 k − 1 − 2 2^{2^{k-1}-2} 22k12
位表示0 1…0 1 11…00 bias+n 11…10 1…01 00…0

2.86

描述十进制
最小的正非规格化数 2 − 63 ∗ 2 1 − ( 2 14 − 1 ) 2^{-63}*2^{1-(2^{14}-1)} 26321(2141)= 2 − 63 ∗ 2 2 − 2 14 2^{-63}*2^{2-2^{14}} 26322214= 2 − 61 − 2 14 2^{-61-2^{14}} 2612143.6452e-4951
最小的正规格化数 2 1 − ( 2 14 − 1 ) 2^{1-(2^{14}-1)} 21(2141)= 2 2 − 2 14 2^{2-2^{14}} 222143.3621e-4932
最大的规格化数 2 2 15 − 1 − ( 2 14 − 1 ) ∗ ( 1 + ( 1 − 2 − 63 ) ) = 2 2 14 − 1 ( 2 − 2 − 63 ) 2^{2^{15}-1-({2^{14}-1})}*(1+(1-2^{-63}))=2^{2^{14}-1}(2-2^{-63}) 22151(2141)(1+(1263))=22141(2263)1.1897e+4932

2.87

bias= 2 5 − 1 2^{5-1} 251-1=15

描述HexMEVD
-01000 0000 0000 0000=0x800000-0-0.0
最小的>2的值0100 0000 0000 0001=0x4001 1 + 2 − 10 1+2^{-10} 1+2101 ( 1 + 2 − 10 ) ∗ 2 (1+2^{-10})*2 (1+210)22.001953125
5120110 0000 0000 0000=0x600019512512.0
最大非规格化数0000 0011 1111 1111=0x03ff 1 − 2 − 10 1-2^{-10} 1210-14 ( 1 − 2 − 10 ) ∗ 2 − 14 (1-2^{-10})*2^{-14} (1210)2146.09755516e-5
-oo1111 1100 0000 0000=0xfc00---oo-oo
0x3BB00011 1011 1011 0000=0x3bb0 123 / 64 123/64 123/64-1 123 / 128 123/128 123/1280.9609375

2.88

A位A值B位B值
1 01110 001-9/161 0110 0010-9/16
0 10110 10113*2^40 1110 101013*2^4
1 00111 110-7/2^101 0000 0111-7/2^10
0 00000 1015/2^170 0000 00011/2^10
1 11011 000-2^121 1110 1111-31*2^3
0 11000 1003*2^80 1111 0000+oo

2.89

A.正确
B.错误,当y为INT_MIN时,x-y溢出
C.正确
D.错误,当x、y、z较大时,32位乘法可能导致结果超出2^53,会导致舍入
E.错误,dx、dz可能为0

2.90

float u2f(unsigned u) {
    return *(float *) &u;
}

float fpwr2(int x) {
    unsigned exp, frac;
    unsigned u;
    
    // 小于最小的非规格化数
    if (x < -149) {
        exp = 0;
        frac = 0;
    }
    // 非规格化数
    else if (x < -126) {
        exp = 0;
		frac = 1 << (x + 149);
    }
    // 规格化数
    else if (x < 128) {
		exp = x + 127;
		frac = 0;
    }
    // 大于最大规格化数
    else {
		exp = 255;
		frac = 0;
    }

    u = exp << 23 | frac;
    return u2f(u);
}

2.91

0x40490FDB->0 10000000 10010010000111111011011
A.0b11.0010010000111111011011
B.0b11.001(001)
C.小数点后第九位,即 2 − 9 2^{-9} 29

2.92

#include <stdio.h>

typedef unsigned float_bits;

float_bits float_negate(float_bits f) {
  unsigned sign = f >> 31;
  unsigned exp = f >> 23 & 0xFF;
  unsigned frac = f & 0x7FFFFF;

  int is_NAN = (exp == 0xFF) && (frac != 0);
  if (is_NAN) {
    return f;
  }
  return ~sign << 31 | exp << 23 | frac;
}

2.93

#include <stdio.h>

typedef unsigned float_bits;

float_bits float_absval(float_bits f) {
  unsigned sign = f >> 31;
  unsigned exp = f >> 23 & 0xFF;
  unsigned frac = f & 0x7FFFFF;

  int is_NAN = (exp == 0xFF) && (frac != 0);
  if (is_NAN) {
    return f;
  }

  return 0 << 31 | exp << 23 | frac;
}

2.94

#include <stdio.h>

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 == 0xFF) 	//如果f为NAN或无穷,直接返回
    return f;

  if (exp == 0)		//非规格化数
  	frac<<=1;
  else if (exp == 0xFF - 1){	//*2后为无穷	
  	exp = 0xFF;
  	frac = 0;
  }
  else	//规格化数
  	exp+=1;
  
  return sign << 31 | exp << 23 | frac;
}

2.95

向偶数舍入:

  • 00 => 0,只 >>1
  • 01 => 0,只 >>1
  • 10 => 1,只 >>1
  • 11 => 1,>>1后加1
#include <stdio.h>

typedef unsigned float_bits;

float_bits float_half(float_bits f) {
  unsigned sign = f >> 31;
  unsigned exp = f >> 23 & 0xFF;
  unsigned frac = f & 0x7FFFFF;
  unsigned rest = f & 0x7FFFFFFF;

  if (exp == 0xFF) {	//如果f为NAN或无穷,直接返回
    return f;
  }
  
  int addition = (frac & 0x3) == 0x3;	//向偶数舍入偏置

  if (exp == 0) {	//非规格化数,<<1后加偏置
    frac >>= 1;		
    frac += addition;
  } else if (exp == 1) {	//规格化数变为非规格化数
    rest >>= 1;
    rest += addition;
    exp = rest >> 23 & 0xFF;
    frac = rest & 0x7FFFFF;
  } else {		//规格化数
    exp -= 1;
  }

  return sign << 31 | exp << 23 | frac;
}

2.96

以正数为例

float_bitsfint
0 00…00 00…00 — 0 01…11 00…000 <= f < 10
0 01…11 00…00 — 0 (01…11+31) 0…001 <= f < 2^31向0舍入
0 (01…11+31) 00…00 — 更大2^31 <= f < oo0x80000000
#include <stdio.h>

typedef unsigned float_bits;

int float_f2i(float_bits f) {
  unsigned sign = f >> 31;
  unsigned exp = f >> 23 & 0xFF;
  unsigned frac = f & 0x7FFFFF;
  unsigned bias = 0x7F;

  int num;
  unsigned E;
  unsigned M;

  if (exp >= 0 && exp < 0 + bias) {		//小于1,向0舍入
    num = 0;
  } else if (exp >= 31 + bias) {		//溢出或(int)f == INT_MIN 
    num = 0x80000000;
  } else {		//向0舍入
    E = exp - bias;
    M = frac | 0x800000;
    if (E > 23) {
      num = M << (E - 23);
    } else {				
      num = M >> (23 - E);
    }
  }

  return sign ? -num : num;
}

2.97

#include <stdio.h>

typedef unsigned float_bits;

/*假设i> 0,计算i的位数,例如0x3 => 2、0xFF => 8、0x80 => 8*/
int bits_length(int i) {
  if ((i & INT_MIN) != 0) {
    return 32;
  }

  unsigned u = (unsigned)i;
  int length = 0;
  while (u >= (1<<length)) {
    length++;
  }
  return length;
}

/*计算l的掩码位数,例如3  => 0x00000007、16 => 0x0000FFFF*/
unsigned bits_mask(int l) {
  return (unsigned) -1 >> (32-l);
}

/*计算float*/
float_bits float_i2f(int i) {
  unsigned sign, exp, frac, rest, exp_sign , round_part;
  unsigned bits, fbits;
  unsigned bias = 0x7F;

  if (i == 0) {
    sign = 0;
    exp = 0;
    frac = 0;
    return sign << 31 | exp << 23 | frac;
  }
  if (i == INT_MIN) {
    sign = 1;
    exp = bias + 31;
    frac = 0;
    return sign << 31 | exp << 23 | frac;
  }

  sign = 0;		//确定符号
  if (i < 0) {
    sign = 1;
    i = -i;
  }

  bits = bits_length(i);	//计算i的位数
  fbits = bits - 1;		//尾数位数		
  exp = bias + fbits;
  
  rest = i & bits_mask(fbits);
  if (fbits <= 23) {
    frac = rest << (23 - fbits);
    exp_sign = exp << 23 | frac;
  } else {		//fbits > 23
    int offset = fbits - 23;
    int round_mid = 1 << (offset - 1);		//用于比较,是否舍入位是10

    round_part = rest & bits_mask(offset);
    frac = rest >> offset;
    exp_sign = exp << 23 | frac;

    if (round_part < round_mid) { 	//向偶数舍入,是否舍入位是00
    	/*无操作*/
    } else if (round_part > round_mid) {	//是否舍入位是11
      exp_sign += 1;		//11…->100…
    } else {		//round_part == round_mid,是否舍入位是10
      if ((frac & 0x1) == 1) {
        exp_sign += 1;		//10…1->10…0
      }
    }
  }

  return sign << 31 | exp_sign;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值