zoukankan      html  css  js  c++  java
  • 深入理解计算机系统_3e 第二章家庭作业 CS:APP3e chapter 2 homework

    初始完成日期:2017.9.26

    许可:除2.55对应代码外(如需使用请联系 randy.bryant@cs.cmu.edu),任何人可以自由的使用,修改,分发本文档的代码。

    本机环境: (有一些需要在多种机器上测试的就没有试验)

    frank@under:~/tmp$ uname -a
    Linux under 4.10.0-35-generic #39~16.04.1-Ubuntu SMP Wed Sep 13 09:02:42 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
    

    2.55

    /*这一段代码的大部分来自http://csapp.cs.cmu.edu/3e/students.html*/
    /* $begin show-bytes */
    #include <stdio.h>
    /* $end show-bytes */
    #include <stdlib.h>
    #include <string.h>
    /* $begin show-bytes */
    
    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]);    //line:data:show_bytes_printf
        printf("
    ");
    }
    
    void show_int(int x) {
        show_bytes((byte_pointer) &x, sizeof(int)); //line:data:show_bytes_amp1
    }
    
    void show_float(float x) {
        show_bytes((byte_pointer) &x, sizeof(float)); //line:data:show_bytes_amp2
    }
    
    void show_pointer(void *x) {
        show_bytes((byte_pointer) &x, sizeof(void *)); //line:data:show_bytes_amp3
    }
    /* $end show-bytes */
    
    
    /* $begin test-show-bytes */
    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);
    }
    /* $end test-show-bytes */
    
    void simple_show_a() {
    /* $begin simple-show-a */
    int val = 0x87654321;
    byte_pointer valp = (byte_pointer) &val;
    show_bytes(valp, 1); /* A. */
    show_bytes(valp, 2); /* B. */
    show_bytes(valp, 3); /* C. */
    /* $end simple-show-a */
    }
    
    void simple_show_b() {
    /* $begin simple-show-b */
    int val = 0x12345678;
    byte_pointer valp = (byte_pointer) &val;
    show_bytes(valp, 1); /* A. */
    show_bytes(valp, 2); /* B. */
    show_bytes(valp, 3); /* C. */
    /* $end simple-show-b */
    }
    
    void float_eg() {
      int x = 3490593;
      float f = (float) x;
      printf("For x = %d
    ", x);
      show_int(x);
      show_float(f);
    
      x = 3510593;
      f = (float) x;
      printf("For x = %d
    ", x);
      show_int(x);
      show_float(f);
    
    }
    
    void string_ueg() {
    /* $begin show-ustring */
    const char *s = "ABCDEF";
    show_bytes((byte_pointer) s, strlen(s)); 
    /* $end show-ustring */
    }
    
    void string_leg() {
    /* $begin show-lstring */
    const char *s = "abcdef";
    show_bytes((byte_pointer) s, strlen(s)); 
    /* $end show-lstring */
    }
    
    void show_twocomp() 
    {
    /* $begin show-twocomp */
        short x = 12345; 
        short mx = -x; 
        
        show_bytes((byte_pointer) &x, sizeof(short)); 
        show_bytes((byte_pointer) &mx, sizeof(short)); 
    /* $end show-twocomp */
    }
    
    int main(int argc, char *argv[])
    {
        int val = 12345;
    
        if (argc > 1) {
    	if (argc > 1) {
    	    val = strtol(argv[1], NULL, 0);
    	}
    	printf("calling test_show_bytes
    ");
    	test_show_bytes(val);
        } else {
    	printf("calling show_twocomp
    ");
    	show_twocomp();
    	printf("Calling simple_show_a
    ");
    	simple_show_a();
    	printf("Calling simple_show_b
    ");
    	simple_show_b();
    	printf("Calling float_eg
    ");
    	float_eg();
    	printf("Calling string_ueg
    ");
    	string_ueg();
    	printf("Calling string_leg
    ");
    	string_leg();
        }
        return 0;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 255.c && ./a.out 
    calling show_twocomp
     39 30
     c7 cf
    Calling simple_show_a
     21
     21 43
     21 43 65
    Calling simple_show_b
     78
     78 56
     78 56 34
    Calling float_eg
    For x = 3490593
     21 43 35 00
     84 0c 55 4a
    For x = 3510593
     41 91 35 00
     04 45 56 4a
    Calling string_ueg
     41 42 43 44 45 46
    Calling string_leg
     61 62 63 64 65 66
    

    数据的低位放置在低地址处,字符串按照顺序从低位地址排列。由此可知该机器为小端字节排序。


    2.56

    show-bytes代码同2.55

    frank@under:~/tmp$ gcc 255.c && ./a.out 192837465
    calling test_show_bytes
     59 77 7e 0b
     76 e7 37 4d
     28 00 4d 93 fc 7f 00 00
    
    

    十进制192837465二进制表示为:1011011111100111011101011001。

    1. 十六进制表示为:0xB7E7759,所以第一行的数据表示这是小端字节排序。

    2. 将二进制小数点左移二十七位,由于单精度浮点数尾数部分只有23位(IEEE 756),多出来的4位1001将丢弃,由于默认的“round to even”,高位将进一位,即1.01101111110011101110110*2^27,bias = 2^7 - 1 = 127,所以阶码部分应为127+27=0x9A,整体为:0,10011010,01101111110011101110110即0x4D37E776。由此看出为小端字节排序。

    3. 该行为小端字节显示的指针。


    2.57

    #include <stdio.h>
    
    typedef unsigned char *byte_pointer;
    
    void show_short(void);
    void show_long(void);
    void show_double(void);
    void show_bytes(byte_pointer start, size_t len);
    
    int main(int argc, char const *argv[])
    {
    	show_short();
    	show_long();
    	show_double();
    	return 0;
    }
    
    void show_bytes(byte_pointer start, size_t len) {
        size_t i;
        for (i = 0; i < len; i++)
    	printf(" %.2x", start[i]);
        printf("
    ");
    }
    
    void show_short(void)
    {
    	short i = 12345;
    	printf("short i = 12345
    ");
    	show_bytes((byte_pointer)&i, sizeof i);
    }
    void show_long(void)
    {
    	long i = 123456789;
    	printf("long i = 123456789
    ");
    	show_bytes((byte_pointer)&i, sizeof i);
    }
    void show_double(void)
    {
    	double i = 123456789.0;
    	printf("double i = 123456789.0
    ");
    	show_bytes((byte_pointer)&i, sizeof i);
    }
    

    编译运行输出:

    frank@under:~/tmp$ ./a.out 
    short i = 12345
     39 30
    long i = 123456789
     15 cd 5b 07 00 00 00 00
    double i = 123456789.0
     00 00 00 54 34 6f 9d 41
    

    2.58

    #include <stdio.h>
    #include <stdint.h>
    
    int is_little_endian(void);
    
    int main(int argc, char const *argv[])
    {
    	return is_little_endian();
    }
    
    int is_little_endian(void)
    {
        int32_t i = 1;
    	unsigned char *p = (unsigned char *)&i;
    	if(*p)
    	{
    		return 1;
    	}
    	return 0;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 258.c && ./a.out ; echo $?
    1
    
    

    2.59

    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
    	int x = 0x89ABCDEF;
    	int y = 0x76543210;
    	printf("0x%.8X
    ", x&0xFF | y&~0xFF);
    	return 0;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 259.c && ./a.out
    0x765432EF
    

    2.60

    #include <stdio.h>
    
    unsigned replace_byte(unsigned x, int i, unsigned char b);
    
    int main(int argc, char const *argv[])
    {
    	printf("%#.8x
    ", replace_byte(0x12345678, 2, 0xAB));
    	printf("%#.8x
    ", replace_byte(0x12345678, 0, 0xAB));
    	return 0;
    }
    
    unsigned replace_byte(unsigned x, int i, unsigned char b)
    {
    	int move = i * 8;
    	return x & ~(0xFF << move) | b << move;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 260.c && ./a.out
    0x12ab5678
    0x123456ab
    
    

    2.61

    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
    
    	int x, y;	/* y means 0 should be returned */
    	int sizeof_int = sizeof(int);
    	
    	/*condition A*/
    	x = ~0;
    	y = 0xFFFFFF00;
    	printf("%d	%d
    ", !(~x), !(~y));
    	
    	/*condition B*/
    	x = 0;
    	y = 0x000000FF;
    	printf("%d	%d
    ", !x, !y);
    	
    	/*condition C*/
    	x = 0x000000FF;
    	y = 0x0000000F;
    	printf("%d	%d
    ", !((x ^ 0xFF)<<((sizeof_int-1)<<3)), !((y ^ 0xFF)<<((sizeof_int-1)<<3)));
    
    	/*condition D*/
    	x = 0x00FFFFFF;
    	y = 0x0FFFFFFF;
    	printf("%d	%d
    ", !(x >> ((sizeof_int-1) << 3)), !(y >> ((sizeof_int-1) << 3)));
    	return 0;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 261.c && ./a.out
    1	0
    1	0
    1	0
    1	0
    

    2.62

    #include <stdio.h>
    
    int int_shifts_are_arithmetic(void);
    int int_shifts_are_logic(void);
    
    int main(int argc, char const *argv[])
    {
    	
    	printf("%d
    ", int_shifts_are_arithmetic());
    	printf("%d
    ", int_shifts_are_logic());
    	return 0;
    }
    
    int int_shifts_are_arithmetic(void)
    {
    	int x = ~0;
    	return x >> 1 == x;
    }
    
    int int_shifts_are_logic(void)
    {
    	unsigned x = ~0;
    	return x >> 1 == x;
    }
    

    我这里由于没有不同字长/不同机器,就暂时用unsigned 代替了一下逻辑右移。

    编译运行输出:

    frank@under:~/tmp$ ./a.out 
    1
    0
    
    

    2.63

    #include <stdio.h>
    
    unsigned srl(unsigned x, int k);
    int sra(int x, int k); 
    
    int main(int argc, char const *argv[])
    {
    	printf("%#.8x
    ", srl(0x80000000, 8));
    	printf("%#.8x
    ", sra(0x80000000, 8));
    	return 0;
    }
    
    unsigned srl(unsigned x, int k) 
    {
    	/* Perform shift arithmetically */
    	unsigned xsra = (int) x >> k;
    	/*思路是由k形成诸如0x00FFFFFF这样的掩码,与xsra进行与操作从而将高位置零*/
    	unsigned w = sizeof(int) << 3;
    	unsigned mask = ~(((1 << k)-1)<<(w-k)); 
      	/*(1 << k)-1能够获得低位连续为1,高位为0的掩码,但是其不能达到全1,于是继续向左移w-k然后取反*/
    	return mask & xsra; 
    }
    
    int sra(int x, int k)
    {
    	/* Perform shift logically */
    	int xsrl = (unsigned) x >> k;
    	/*这个题目的关键点是判断符号位是否为1,通过test &= xsrl,test为零如果符号位为0,否则test不变(处于符号位位置*/
    	unsigned w = sizeof(int) << 3;
    	int test = 1 << (w-1-k);
    	test &= xsrl;
    	int mask = ~(test - 1);
      	/*test为零时,~(test - 1)为全零,不会改变xsrl*/
    	return mask | xsrl;
    }
    

    这个题目卡了一会,主要是不能用右移比较麻烦。

    编译运行输出:

    frank@under:~/tmp$ gcc 263.c && ./a.out
    0x00800000
    0xff800000
    

    2.64

    #include <stdio.h>
    
    int any_odd_one(unsigned x);
    
    int main(int argc, char const *argv[])
    {
    	printf("%d	%d
    ", any_odd_one(1011), any_odd_one(1024));
    	return 0;
    }
    
    int any_odd_one(unsigned x)
    {
    	unsigned sizeof_unsigned = sizeof(unsigned);
    	unsigned w = sizeof_unsigned << 3;
    	return !!(x << (w-1));
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 264.c && ./a.out
    1	0
    
    

    2.65 (终于碰见个四星的。。。)

    /*二分法/加法无法达到要求
    第一次尝试:
    int odd_ones(unsigned x);
    
    int main(int argc, char const *argv[])
    {
    	int sizeof_int = sizeof(int);
    	return 0;
    }
    
    int odd_ones(unsigned x)
    {
    	int mask1 = 0x55555555;
    	int mask2 = 0x33333333;
    	int mask3 = 0x0F0F0F0F;
    	int mask4 = 0x00FF00FF;
    	int mask_odd_or_even = 1
    
    	x = ((x >> 1)& mask1) + (x & mask1);
    	x = ((x >> 2)& mask2) + (x & mask2);
    	x = ((x >> 4)& mask3) + (x & mask3);
    	x = ((x >> 8)& mask4) + (x & mask4);
    	x = (x >> 16) + x;
    	return x & mask_odd_or_even;
    }
    第二次尝试:
    int odd_ones(unsigned x);
    
    int main(int argc, char const *argv[])
    {
    	unsigned x1 = 0xFF00FF00;
    	unsigned x2 = 0xFF01FF00;
    	printf("%d	%d
    ", odd_ones(x2), odd_ones(x1));
    	return 0;
    }
    
    int odd_ones(unsigned x)
    {
    	int mask1 = 0x55555555;
    	int mask2 = 0x33333333;
    	int mask3 = 0x0F0F0F0F;
    	int mask4 = 0x00FF00FF;
    	int mask_odd_or_even = 1;
    
    	x = ((x >> 1)& mask1) + (x & mask1);
    	x = x & mask1;
    	x = (x >> 16) + x;
    	x = x & mask1;
    	x = (x >> 8) + x;
    	x = x & mask1;
    	x = (x >> 4) + x;
    	x = x & mask1;
    	x = (x >> 2) + x;
    	return x & mask_odd_or_even;
    }
    */
    //第三次尝试:使用二分法/异或
    #include <stdio.h>
    
    int odd_ones(unsigned x);
    
    int main(int argc, char const *argv[])
    {
    	unsigned x1 = 0xFF00FF00;
    	unsigned x2 = 0xFF01FF00;
    	printf("%d	%d
    ", odd_ones(x2), odd_ones(x1));
    	return 0;
    }
    
    int odd_ones(unsigned x)
    {
    	x = x ^ (x >> 16);
    	x = x ^ (x >> 8);
    	x = x ^ (x >> 4);
    	x = x ^ (x >> 2);
    	x = x ^ (x >> 1);
    	return x & 1;
    }
    

    这个题的关键点在于如何表示偶数(将所有“1”相加末位为0)以及类似二分法的相加方法,同时注意到每次需要用用掩码将以前的高位置零。

    这个题目是不会有“溢出”的情况的,因为1+1=10,10+10=0100,0100+0100=00001000......datalab实验里有一个相似的题目,那个题目更难一些。

    以上想法在满足“Your code should contain a total of at most 12 arithmetic, bitwise, and logical
    operations.”时出现了问题,根本原因在于二分法需要顾及到低位相加可能产生的进位,所以每次都需要用掩码将特定的高位置零,思路有点受到之前datalab实验的束缚(那个是要计算“1”的总数目)。这里计算的是“1”的数目的奇偶,不用考虑进位,异或运算是最佳选择,因为1+1和0+0均产生0(代表偶数),1+0和0+1均产生1(代表奇数)。

    编译运行输出:

    frank@under:~/tmp$ gcc 265.c && ./a.out 
    1	0
    
    

    2.66

    #include <stdio.h>
    #include <limits.h>
    
    /*
     * Generate mask indicating leftmost 1 in x. Assume w=32.
     * For example, 0xFF00 -> Ox8000, and Ox6600 --> Ox4000.
     * If x = 0, then return 0.
     */
    
    int leftmost_one(unsigned x);
    
    int main(int argc, char const *argv[])
    {
    	printf("%#.8x
    ", leftmost_one(0xFF00));
    	printf("%#.8x
    ", leftmost_one(0x6600));
    	printf("%#.8x
    ", leftmost_one(0x88886600));
    	printf("%#.8x
    ", leftmost_one(0));
    	return 0;
    }
    
    int leftmost_one(unsigned x)
    {
    	unsigned sizeof_unsigned = sizeof(unsigned);
    	unsigned w = sizeof_unsigned << 3;
    	x |= x >> 1;
    	x |= x >> 2;
    	x |= x >> 4;
    	x |= x >> 8;
    	x |= x >> 16;
    	return x & ((~x >> 1)|INT_MIN);
    }
    /*
     * Your code should contain a total of at most 15 arithmetic, bitwise, and logical
     * operations.
     * Hint: First transform x into a bit vector of the form [O · · · 011 · . · 1].
     */
    

    最后与INT_MIN做或运算是为了处理0x80000000这种边界情况,在这种情况下,~x >> 1由于没有更高位,而x又是unsigned类型,所以最高位会是0而非1,为了适应这种情况,强制将~x >> 1最高位置1。

    编译运行输出:

    frank@under:~/tmp$ gcc 266.c && ./a.out 
    0x00008000
    0x00004000
    0x80000000
    00000000
    
    

    另外,Web Asides http://csapp.cs.cmu.edu/3e/waside/waside-tneg.pdf 上面有一个利用-x和x的区别在于除最右1之前位翻转的特性求rightmost_one: x&-x, 有时间可以看看。


    2.67

    A:

    (C11, 6.5.7p3) "If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined"

    B:

    #include <stdio.h>
    
    int int_size_is_32();
    
    int main(int argc, char const *argv[])
    {
    	printf("%d
    ", int_size_is_32());
    	return 0;
    }
    
    int int_size_is_32()
    {
    	int set_msb = 1 << 31;
    	int beyond_msb = set_msb;
    	beyond_msb <<= 1;
    	return set_msb && !beyond_msb;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 267.c && ./a.out 
    1
    
    

    C:

    #include <stdio.h>
    
    int int_size_is_32();
    
    int main(int argc, char const *argv[])
    {
    	printf("%d
    ", int_size_is_32());
    	return 0;
    }
    
    int int_size_is_32()
    {
    	int set_msb = 1 << 15;
    	set_msb <<= 15;
    	set_msb <<= 1;
    	int beyond_msb = set_msb;
    	beyond_msb <<= 1;
    	return set_msb && !beyond_msb;
    }
    

    2.68

    #include <stdio.h>
    
    int lower_one_mask(int n);
    
    int main(int argc, char const *argv[])
    {
    	printf("%#.8x
    ", lower_one_mask(6));
    	printf("%#.8x
    ", lower_one_mask(17));
    	return 0;
    }
    
    int lower_one_mask(int n)
    {
    	int sizeof_int = sizeof(int);
    	unsigned x = ~0;
    	x >>= ((sizeof_int << 3) - n);
    	return x;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 268.c && ./a.out
    0x0000003f
    0x0001ffff
    
    

    2.69

    #include <stdio.h>
    
    unsigned rotate_left(unsigned x, int n);
    
    int main(int argc, char const *argv[])
    {
    	unsigned x = 0x12345678;
    	printf("%#.8x
    ", rotate_left(x, 0));
    	printf("%#.8x
    ", rotate_left(x, 4));
    	printf("%#.8x
    ", rotate_left(x, 20));
    	return 0;
    }
    
    unsigned rotate_left(unsigned x, int n)
    {
    	unsigned sizeof_unsigned = sizeof(unsigned);
    	unsigned w = sizeof_unsigned << 3;
    	unsigned mask = ((1 << n)-1) << (w-n);
    	unsigned cache = (mask & x) >> (w-n);
    	x <<= n;
    	return x | cache;
    }
    

    关键点在于掩码的产生和移除位数据的保存。

    编译运行输出:

    frank@under:~/tmp$ gcc 269.c && ./a.out 
    0x12345678
    0x23456781
    0x67812345
    
    

    2.70

    #include <stdio.h>
    #include <limits.h>
    
    int fits_bits(int x, int n);
    
    int main(int argc, char const *argv[])
    {
        /*test short and 31bits*/
    	printf("%d
    ", fits_bits(-32768, 16));
    	printf("%d
    ", fits_bits(32767, 16));
    	printf("%d
    ", fits_bits(INT_MAX, 32));
    	printf("%d
    ", fits_bits(INT_MIN, 32));
    	printf("%d
    ", fits_bits(0, 16));
    	printf("%d
    ", fits_bits(0, 32));
    	printf("%d
    ", fits_bits(32768, 16));
    	printf("%d
    ", fits_bits(-32769, 16));
    	printf("%d
    ", fits_bits(INT_MIN, 31));
    	printf("%d
    ", fits_bits(INT_MAX, 31));
    	return 0;
    }
    
    int fits_bits(int x, int n)
    {
    	unsigned sizeof_int = sizeof(int);
    	unsigned w = sizeof_int << 3;
    	int y = x << (w-n) >> (w-n);
    	return y == x;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 270.c && ./a.out 
    1
    1
    1
    1
    1
    1
    0
    0
    0
    0
    
    

    2.71

    /* Declaration of data type where 4 bytes are packed into an unsigned */
    typedef unsigned packed_t;
    /* Extract byte from word. Return as signed integer */
    int xbytte(packed_t word, int bytenum);
    /*That is, the function will extract the designated byte and sign extend it to be
    a 32-bit int.
    Your predecessor (who was fired for incompetence) wrote the following code:*/
    
    //Failed attempt at xbyte:
    
    int xbyte(packed_t word, int bytenum)
    {
    return (word>> (bytenum << 3)) & OxFF;
    }
    //A. What is wrong with this code?
    
    //B. Give a correct implementation of the function that uses only left and right
    //shifts, along with one subtraction.
    

    A:

    当取出的字节为负数时,由于原操作“粗暴”的将高位置零,会返回一个错误的正值。

    B:

    #include <stdio.h>
    
    typedef unsigned packed_t;
    
    int xbytte(packed_t word, int bytenum);
    
    int main(int argc, char const *argv[])
    {
    	packed_t word = 0x8008FF00;
    	printf("%d
    ", xbytte(word, 0));
    	printf("%d
    ", xbytte(word, 1));
    	printf("%d
    ", xbytte(word, 2));
    	printf("%d
    ", xbytte(word, 3));
    	return 0;
    }
    
    int xbytte(packed_t word, int bytenum)
    {
    	unsigned left_move = (3 - bytenum) << 3;
    	unsigned right_move = (3) << 3;
    	return (int)word << left_move >> right_move;
    }
    
    

    编译运行输出:

    frank@under:~/tmp$ gcc 271.c && ./a.out 
    0
    -1
    8
    -128
    

    2.72

    /*BUGGY: Copy integer into buffer if space is available */
    void copy_int(int val; void *buf, int maxbytes) 
    {
    	if (maxbytes-sizeof(val) >= 0)
    		memcpy(buf, (void*) &val, sizeof(val));
    }
    

    A:

    sizeof返回的类型为size_t:

    According to the 1999 ISO C standard (C99), size_t is an unsigned integer type of at least 16 bit (see sections 7.17 and 7.18.3).

    size_tis an unsigned data type defined by several C/C++ standards, e.g. the C99 ISO/IEC 9899 standard, that is defined in stddef.h.1 It can be further imported by inclusion ofstdlib.h as this file internally sub includes stddef.h.

    所以maxbytes-sizeof(val)将一直转化为无符号数并永远大于等于零。

    B:

    void copy_int(int val; void *buf, int maxbytes) 
    {
      	if(maxbytes < 0)
          	return;
    	if (maxbytes >= sizeof(val))
    		memcpy(buf, (void*) &val, sizeof(val));
    }
    

    2.73

    #include <stdio.h>
    #include <limits.h>
    
    int saturating_add(int x, int y);
    
    int main(int argc, char const *argv[])
    {
    	printf("%d
    ", saturating_add(123456, -54321));
    	printf("%d
    ", saturating_add(2147483647, 1));
    	printf("%d
    ", saturating_add(-2147483648, -1));
    	return 0;
    }
    
    int saturating_add(int x, int y)
    {
    	unsigned sizeof_int = sizeof(int);
    	unsigned w = sizeof_int << 8;
    	int i = (x ^ y) >> (w-1);//+-:FFFFFFFF ++/--:00000000
    	int j = ((x+y) ^ x) >> (w-1);//overflow:FFFFFFFF otherwise:00000000
    	int k = x >> (w-1);//+:00000000 -:FFFFFFFF
    	return (i & (x + y)) + (~i & (j & ( (~k & INT_MAX) + (k & INT_MIN) )));
    }
    

    解释一下i j k:这三个变量和与运算结合用来做“判断语句”,i通过x,y是否异号判断是否可能溢出,j通过结果和加数的符号判断在同号的情况下是否发生溢出。k判断是应该返回INT_MAX 还是 INT_MIN。

    编译运行输出:

    frank@under:~/tmp$ gcc 273.c && ./a.out 
    69135
    2147483647
    -2147483648
    

    2.74

    #include <stdio.h>
    #include <limits.h>
    
    int tsub_ok(int x, int y);
    
    int main(int argc, char const *argv[])
    {
    	printf("%d
    ", tsub_ok(123456, 54321));
    	printf("%d
    ", tsub_ok(2147483647, -1));
    	printf("%d
    ", tsub_ok(-2147483648, 1));
    	return 0;
    }
    
    int tsub_ok(int x, int y)
    {
    	unsigned sizeof_int = sizeof(int);
    	unsigned w = sizeof_int << 8;
    	y = -y;
    
    	int i = (x ^ y) >> (w-1);//+-:FFFFFFFF ++/--:00000000
    	int j = ((x+y) ^ x) >> (w-1);//overflow:FFFFFFFF otherwise:00000000
    	return i || ~j;
    }
    

    原理与2.73类似。

    编译运行输出:

    frank@under:~/tmp$ gcc 274.c && ./a.out 
    1
    0
    0
    
    

    2.75

    #include <stdio.h>
    #include <stdint.h>
    #include <stdbool.h>
    
    unsigned unsigned_high_prod(unsigned x, unsigned y);
    int signed_high_prod(int x, int y);
    
    int main(int argc, char const *argv[])
    {
    	/* code */
    	return 0;
    }
    
    unsigned unsigned_high_prod(unsigned x, unsigned y)
    {
    	unsigned w = sizeof(int32_t) << 3;
    	int64_t signed_total_prod = signed_high_prod(x, y);
    	signed_total_prod <<= w;
    	signed_total_prod += x*y;
    	bool x_w = x < 0 ? true : false;
    	bool y_w = y < 0 ? true : false;
    	int64_t unsigned_total_prod = signed_total_prod + ((x_w*(int)y + y_w*(int)x)<<w) + x_w*y_w<<(w*2);
    	return (unsigned)(unsigned_total_prod>>w);	
    }
    

    原理参见树上2.18等式。


    2.76

    void *calloc(size_t nmemb, size_t size)
    {
    	void *p;
    	if(!(nmemb*size) || !(p = malloc(size*nmemb)))
    		return NULL;
    	else if (((size_t)(nmemb*size))/size != nmemb) 
                    /* __builtin_umull_overflow() works too */
    	{
                    /* Thanks to zhzhwz who found a forget-to-free problem here. 
                       Maybe we should write a free_and_return_NULL block 
                       and goto it specifically.
                    */
    		fprintf(stderr, "size*nmemb overflow size_t.
    ");
                    free(p);
                    return NULL;
    	}
    	else
    	{
    		memset(p, 0, size*nmemb);
    		return p;
    	}
    }
    

    2.77

    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
    	unsigned sizeof_int = sizeof(int);
    	unsigned w = sizeof_int << 3;
    	int x = 1;
    	printf("%d
    ", (x << 4) + x);//k=17
    	printf("%d
    ", x - (x << 3));//k=-7
    	printf("%d
    ", (x << 6) - (x << 2));//k=60
    	printf("%d
    ", (x << 4) - (x << 7));//k=-112
    	return 0;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 277.c && ./a.out 
    17
    -7
    60
    -112
    

    2.78

    #include <stdio.h>
    
    int divide_power2(int x, int k);
    
    int main(int argc, char const *argv[])
    {
    	printf("%d
    ", divide_power2(1024, 2));
    	printf("%d
    ", divide_power2(5, 2));
    	printf("%d
    ", divide_power2(-1024, 2));
    	printf("%d
    ", divide_power2(-5, 2));
    	return 0;
    }
    
    int divide_power2(int x, int k)
    {
    	int bias = (1 << k) - 1;
    	unsigned sizeof_int = sizeof(int);
    	unsigned w = sizeof_int << 3;
    	int judge = x >> (w-1); //-:FFFFFFFF +:00000000
    	return (judge & ((x + bias) >> k)) + (~judge & (x >> k));
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 278.c && ./a.out 
    256
    1
    -256
    -1
    
    

    2.79

    int mul3div4(int x)
    {
    	int k = 2;
    	int bias = (1 << k) - 1;
    	unsigned sizeof_int = sizeof(int);
    	unsigned w = sizeof_int << 3;
    	x = (x << 1) + x;
    	int judge = x >> (w-1); //-:FFFFFFFF +:00000000
    	return (judge & ((x + bias) >> k)) + (~judge & (x >> k));
    }
    

    2.80

    [BUG] threefourths(3)应计算得到2,代码将会得到0,可以参考 2.80
    From: zhzhwz

    int threefourths(int x)
    {
      	int k = 2;
    	int bias = (1 << k) - 1;
    	unsigned sizeof_int = sizeof(int);
    	unsigned w = sizeof_int << 3;
    	int judge = x >> (w-1); //-:FFFFFFFF +:00000000
    	x = (judge & ((x + bias) >> k)) + (~judge & (x >> k));
      	return (x << 1) + x;
    }
    

    参考2.78


    2.81

    A:

    x = ~0 << k;
    

    B:

    x = ~(~0 << k) << j;
    

    2.82

    A: when x = INT_MIN, y = 0. Yields 0

    B: It always yields 1. Say, "mod" = mod 2^32. LEFT = ((((((x+y)mod)*16)mod)+y)mod)-x)mod = (17y + 15x)mod. RIGHT = (17*y)mod + (15*x)mod = (17y + 15x)mod.

    C: It always yields 1. LEFT = (~x + 1) + (~y + 1) - 1 = -x + -y - 1. RIGHT = ~(x+y) + 1 - 1 = -(x+y) - 1 = -x + -y - 1.

    D: It always yields 1. Since whether a integer type data is a int or unsigned doesn't influence the implementations of subtraction or unary minus operators.

    (写到一半突然发现是用英文写的,可能是看教材影响的。。。)

    E: 永远产生1,因为x先向右移动,再向左移动相同的位置,所以数据的高位不会受到影响。但是如果低两位有1的话,会都变为0。因为低位的1在补码中无论对于负数还是正数都是加的。所以对于正数来说,值会变小或者不变;对于0来说,值会不变;对于负数来说,值会变小或者不变。


    2.83

    A:

    根据提示:Y = x*2^k - x 即 x = Y/(2^k - 1)

    B:

    由A:(a) Y = 101 = 5, k = 3, x = 5/7 (b) Y = 0110 = 6, k = 4, x = 2/5 (c) Y = 010011 = 19, k = 6, x = 19/63


    2.84

    #include <stdio.h>
    
    int float_le(float x, float y);
    unsigned f2u(float x);
    
    int main(int argc, char const *argv[])
    {
    	printf("%d
    ", float_le((float)1.11, (float)1.10));
    	printf("%d
    ", float_le((float)-1.2, (float)3.0));
    	printf("%d
    ", float_le((float)1.3, (float)1.3));
    	printf("%d
    ", float_le((float)0, (float)0));
    	printf("%d
    ", float_le((float)-1.1, (float)0));
    	printf("%d
    ", float_le((float)0, (float)1.1));
    	return 0;
    }
    
    unsigned f2u(float x)
    {
    	return *(unsigned*)&x;
    }
    
    int float_le(float x, float y) 
    {
    	unsigned ux = f2u(x);
    	unsigned uy = f2u(y);
    	/*Get the sign bits*/
    	unsigned sx = ux >> 31; //+:0 -:1
    	unsigned sy = uy >> 31;
    
    	/* Give an expression using only ux, uy, sx, and sy */
    	return (sx ^ sy) ? (sx ? 1 : 0)/*-+ +-*/ : (sx ? (ux>=uy) : (ux<=uy))/*-- ++*/;
    }
    

    编译运行输出:

    frank@under:~/tmp$ gcc 284.c && ./a.out 
    0
    1
    1
    1
    1
    1
    
    

    [PATCH] +0和-0不等
    From: zhzhwz

    @@ -11,6 +11,7 @@ int main(int argc, char const *argv[])
    printf("%d
    ", float_le((float)0, (float)0));
    printf("%d
    ", float_le((float)-1.1, (float)0));
    printf("%d
    ", float_le((float)0, (float)1.1));
        printf("%d
    ", float_le((float)(1/1e100), (float)(-1/1e100)));
    return 0;
    }
    @@ -28,5 +29,5 @@ int float_le(float x, float y)
    unsigned sy = uy >> 31;
    /* Give an expression using only ux, uy, sx, and sy */
        return (sx ^ sy) ? (sx ? 1 : 0)/-+ +-/ : (sx ? (ux>=uy) : (ux<=uy))/-- ++/;
        return (ux << 1 == 0 && uy << 1 == 0)/all 0/ || ((sx ^ sy) ? (sx ? 1 : 0)/-+ +-/ : (sx ? (ux>=uy) : (ux<=uy))/-- ++/);
    }
     No newline at end of file
    
    

    2.85

    bias = 2^(k-1) - 1 suppose that k <= n

    A:

    E = 0b10+bias, M = 0b1.11, f = ob1100*, V = 1.0

    bit representation: 0, 0b10+bias, 1100*

    B:

    E = n+bias, M = 0b1.11*, f = 0b11*, V = 2^(n+1)-1

    bit representation: 0, n+bias, 11*

    C:

    The smallest positive normalized value : E = 0b00*1, M = 0b1.00*, f = 0b00*, V = 1.0

    So the reciprocal is exactly the same number.


    2.86

    bias = 2^14 - 1

    Smallest positive denormalized:

    Value: 0, 00*, 0, 00*1 Decimal:2(2-214) * 2^(-63)

    Smallest positive normalized:

    Value: 0, 00*1, 1, 00* Decimal:2(2-214)

    Largest normalized:

    Value: 0, 11*0, 1, 11* Decimal: 2(214-1) * (2-2^(-63))


    2.872.88本来在Typora上是用表格写的,上传上来好像有格式问题,将就看一下 ; )

    2.87

    | Description | Hex | M | E | V | D |
    | -0 | 8000 | 0 | -14 | 0 | 0 |
    | Smallest value > 2 | 4001 | 1025/1024 | 1 | 10252^-8 | 2.001953 |
    | 512 | 6000 | 1 | 9 | 1
    2^9 | 512.000000 |
    | Largest denormalized | 0311 | 1023/1024 | -14 | 10232^-24 | 0.000061 |
    | negative infinite | FC00 | - | - | - | -inf |
    | 3BB0 | 3BB0 | 124/64 | -1 | 31
    2^-5 | 0.968750 |


    2.88

    | Format A | Format A | Format B | Format B |
    | Bits | Value | Bits | Value |
    | 1 01111 001 | -9/8 | 1 0111 0010 | -9/8 |
    | 0 10110 011 | 112^4 | 0 1110 0110 | 112^4 |
    | 1 00111 010 | -52^-10 | 1 0000 0101 | -52^-10 |
    | 0 00000 111 | 72^-17 | 0 0000 0001 | 2^-10 |
    | 1 11100 000 | -2^13 | 1 1110 1111 | -31
    2^3 |
    | 0 10111 100 | 32^7 | 0 1110 1111 | 312^3 |


    2.89

    A:总是返回1.因为int到double不会有精度上的损失,所以x,dx转float(损失精度)的结果是一样的。

    B:不总是返回1.如x=INT_MIN,Y=1。

    [PATCH] 注意2.89的C中dz是由一个整数转换来的,因此不会取到1e-30
    From: zhzhwz

    - C:不总是返回1.浮点数不满足结合律,如dx=1e30, dy=-1e30, dz=1e-30。
    + C:由于int转换为double不会有精度上的损失,且在int范围内使用double做加法得到的结果一定是精确的(这是因为double的尾数足以容纳32bit),因此满足结合律。
    

    D:不总是返回1.原因同上。例如dx与dy互为倒数且dy*dz=+infinite。

    E:不总是返回1.例如dx=1.0, dz=0.0 。


    2.90

    float fpwr2(int x)
    {
    	/* Result exponent and fraction */
    	unsigned exp, frac;
    	unsigned u;
    	if (x < -149)
    	{
    		/* Too small. Return 0.0 */
    		exp = 0;
    		frac = 0;
    	} 
    	else if (x < -126) 
    	{
    		/* Denormalized result */
    		exp = 0;
    		frac = 1 << (149 + x);
    	}
    	else if (x < 128)
    	{
    		/* Normalized result. */
    		exp = x + 127;
    		frac = 0;
    	}
    	else
    	{
    		/* Too big. Return +oo */
    		exp = 0xFF;
    		frac = 0; 
    	}
    	
    	/*Pack exp and frac into,32 bits */
    	u = exp << 23 | frac;
    	/* Return as float */
    	return u2f(u);
    }
    

    2.91

    0x 40490FDB = 0b 0100 0000 0100 1001 0000 1111 1101 1011 = 0,10000000,10010010000111111011011

    A: 10010010000111111011011

    B: (详情可见)2.83 y=1, k=3, 即 0b11.(001)*

    C: 0x4049039b 0x40492492 从高位向低位第19个。



    浮点数部分由于时间所限,没有进行相关测试,思路大致应该是对的,可能会有一些边界/特殊情况会产生问题,欢迎指出。

    2.92

    float_bits float_negate(float_bits f)
    {
    	unsigned sign = f >> 31;
    	unsigned exp = f >> 23 & 0xFF;
    	unsigned frac = f & 0x7FFFFF;
    	if(!(exp ^ 0xFF) && frac)
    	{
    		return f;
    	}
    	else
    	{
    		sign = !sign;
    		return (sign << 31) | (exp << 23) | frac;
    	}
    }
    

    2.93

    float_bits float_absval(float_bits f)
    {
    	unsigned exp = f >> 23 & 0xFF;
    	unsigned frac = f & 0x7FFFFF;
    	if(!(exp ^ 0xFF) && frac)
    	{
    		return f;
    	}
    	return (exp << 23) | frac;
    }
    

    2.94

    float_bits float_twice(float_bits f)
    {
    	unsigned sign = f >> 31;
    	unsigned exp = f >> 23 & 0xFF;
    	unsigned frac = f & 0x7FFFFF;
    	if(!(exp ^ 0xFF))
    	{
    		if (frac)//NaN
    		{
    			return f;
    		}
    		/*else
    		{
    			return (sign << 31) | (exp << 23) | frac;//infinite
    		}*/
    	}
    	else//Denormalnized and normalized
    	{
    		if (exp)//Normalnized
    		{
    			if (!(frac >> 22))
    			{
    				frac <<= 1;
    				//return (sign << 31) | (exp << 23) | frac;
    			}
    			else if (!(exp ^ 0xFE))
    			{
    				++exp;
    				//return (sign << 31) | (exp << 23) | frac;
    			}
    			else//overflow
    			{
    				frac = 0;
    				exp = 0xFF;
    				//return (sign << 31) | (exp << 23) | frac;
    			}
    		}
    		else//Denormalized
    		{
    			if (!(frac >> 22))
    			{
    				frac <<= 1;
    				//return (sign << 31) | (exp << 23) | frac;
    			}
    			else//Turn to Normalized
    			{
    				++exp;
    				frac = frac << 10 >> 9;//set the 23th bit of frac to 0 and then left shift one bit.(注释是必要的。。。过了几天看这一段的时候自己也没弄懂,忘了这里frac是一个unsigned。。。) 
    				//return (sign << 31) | (exp << 23) | frac;
    			}
    		}
    	}
    	return (sign << 31) | (exp << 23) | frac;
    }
    

    2.95

    float_bits float_half(float_bits f)
    {
    	unsigned sign = f >> 31;
    	unsigned exp = f >> 23 & 0xFF;
    	unsigned frac = f & 0x7FFFFF;
    	if(!(exp ^ 0xFF))
    	{
    		if (frac)//NaN
    		{
    			return f;
    		}
    		/*else
    		{
    			return (sign << 31) | (exp << 23) | frac;//infinite
    		}*/
    	}
    	else//Denormalnized and normalized
    	{
    		if (exp)//Normalnized
    		{
    			if (exp != 1)
    			{
    				--exp;
    				//return (sign << 31) | (exp << 23) | frac;
    			}
    			else//Turn to Denormalnized
    			{
    				if (frac)//maybe need to round to even
    				{
    					if ((frac >> 1)&1)
    					{
    						++frac;
    						frac >>= 1;
    						frac |= 0x400000;
    						--exp;
    					}
    					else
    					{
    						frac >>=1;
    						frac |= 0x400000;
    						--exp;
    					}
    				}
    			}
    		}
    		else//Denormalized
    		{
    			if (frac)//maybe need to round to even
    			{
    				if ((frac >> 1)&1)
    				{
                                            ++frac;
    					frac >>= 1;
    				}
    				else
    				{
    					frac >>= 1;
    				}
    			}
    		}
    	}
    	return (sign << 31) | (exp << 23) | frac;
    }
    

    2.96

    int float_f2i(float_bits f)
    {
    	unsigned sign = f >> 31;
    	unsigned exp = f >> 23 & 0xFF;
    	unsigned frac = f & 0x7FFFFF;
    	unsigned bias = 127;
    	int flag = 0;
    	if(sign)//<=0
    	{
    		if (f < 0xBF800000)//>-1
    		{
    			return 0;
    		}
    		else if (f <= 0xCF000000)
    		{
    			if (f == 0xCF000000)//INT_MIN
    			{
    				return INT_MIN;//0x80000000;
    			}
    			else
    			{
    				f &= 0x7FFFFFFF;//first treat it as a positive number
    				flag = 1;
    				goto A;
    				B:
    				return (~f + 1);//-(+int)
    			}
    		}
    		else//overflow/-infinite/NaN
    		{
    			return 0x80000000;
    		}
    	}
    	else//>=0
    	{
    		if (f < 0x3F800000)//<1//Denormalnized->0
    		{
    			return 0;
    		}
    		else if (f <= 0x4EFFFFFF)
    		{
    			A:
    			frac |= 0x800000;
    			unsigned move = 23 - (exp-bias);
    			if (flag)//jumped from a negative number
    			{
    				f = move >= 0 ? frac >> move : frac << -move;
    				goto B;
    			}
    			else
    			{
    				return move >= 0 ? frac >> move : frac << -move; 
    			} 
    		}
    		else//overflow/+infinite/NaN
    		{
    			return 0x80000000;
    		}
    	}
    }
    
    标准答案
     /* Compute (int) f. If conversion causes overflow or f is NaN, return 0x80000000 */
     int float_f2i(float_bits f) {
     unsigned sign = f >> 31;
     unsigned exp = (f >> 23) & 0xFF;
     unsigned frac = f & 0x7FFFFF;
     /* Create normalized value with leading one inserted, and rest of significand in bits 8--30./
     unsigned val = 0x80000000u + (frac << 8);
     if (exp < 127) {
     /* Absolute value is < 1 */
     return (int) 0;
     }
     if (exp > 158)
     /* Overflow */
     return (int) 0x80000000u;
     /* Shift val right */
     val = val >> (158 - exp);
     /* Check if out of range */
     if (sign) {
     /* Negative */
     return val > 0x80000000u ? (int) 0x80000000u : -(int) val;
     } else {
     /* Positive */
     return val > 0x7FFFFFFF ? (int) 0x80000000u : (int) val;
     }
     }
    

    2.97

    int leftmost_one(unsigned x)
    {
    	unsigned sizeof_unsigned = sizeof(unsigned);
    	unsigned w = sizeof_unsigned << 3;
    	x |= x >> 1;
    	x |= x >> 2;
    	x |= x >> 4;
    	x |= x >> 8;
    	x |= x >> 16;
    	return x & ((~x >> 1)|INT_MIN);
    }
    
    float_bits float_i2f(int i)
    {
    	unsigned sign = 0;
    	unsigned exp = 0;
    	unsigned frac = 0;
    	unsigned bias = 127;
    	if (i == INT_MIN)
    	{
    		return 0xCF000000;
    	}
    	else//treat negative as positive
    	{
    		if (i < 0)
    		{
    			sign = 1;
    			i = -i;
    		}
    		int mask = leftmost_one(i);
    		int move = 0;
    		if (mask >= 0x00800000)//rightshift
    		{
    			while(mask != 0x00800000)
    			{
    				mask >>= 1;
    				++move;
    			}
    
    			if ((i & ((1 << (move+1)) - 1)) > (1 << move))//round to even(>1/2)
    			{
    				i >>= move;
    				i += 1;
    			}
    			else if((i & ((1 << (move+1)) - 1)) < (1 << move))//(<1/2)
    			{
    				i >>= move;
    			}
    			else// 1/2
    			{
    				if ((i >> move)&1)//round to even
    				{
    					i >>= move;
    					i += 1;
    				}
    				else
    				{
    					i >>= move;
    				}
    			}
    		}
    		else//leftshift
    		{
    			while(mask != 0x00800000)
    			{
    				mask <<= 1;
    				--move;
    			}
    			i <<= -move;
    		}
    		frac = i & 0x7FFFFF;//Discard the 24th bit one
    		exp = bias + 22 + move;
    	}
    	return (sign << 31) | (exp << 23) | frac;
    }
    

    用到了2.66产生标志整数最高位1掩码,从而判断应该左移或者右移多少位。

  • 相关阅读:
    【0711作业】使用封装实现企鹅
    【0711作业】模拟选民投票
    【0709作业】判断三个数是否能组成三角形以及组成的三角形类型
    【0709作业】简易的购房商贷月供计算器
    【0708】(OOP)用户密码管理
    数据库基础
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
  • 原文地址:https://www.cnblogs.com/liqiuhao/p/7596539.html
Copyright © 2011-2022 走看看