[第二章] 深入理解计算机系统第三版 家庭作业参考答案

代码均经过或复杂或简单的测试,如果有什么不对的地方,敬请留下评论


第二章

2.55 2.56 2.57

#include <stdio.h>
#include <stdlib.h>
#include <string.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]);    //line:data:show_bytes_printf
	printf("\n");
}

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
}

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) {
	int ival = val;
	float fval = (float)ival;
	int *pval = &ival;
	short sval = (short)ival;
	long lval = (long)ival;
	double dval = (double)ival;
	show_int(ival);
	show_float(fval);
	show_pointer(pval);
	show_short(sval);
	show_long(lval);
	show_double(dval);
}

int main() {
	test_show_bytes(123);
	printf("\n");
	test_show_bytes(32);
	getchar();
}

2.58

int is_little_endian() {
	int a = 1;
	byte_pointer p = &a;
	return p[0];
}

2.59

#include <stdio.h>

int main() {
	int x = 0x89ABCDEF;
	int y = 0x76543210;
	printf("0x%x\n", (0xFF & x) + (0xFFFFFF00 & y));
	system("pause");
}

2.60

#include <stdio.h>

unsigned replace_byte(unsigned x, int i, unsigned char b) {
	char *p = &x;
	p[i] = b;
	return x;
}
int main() {
	printf("0x%x\n", replace_byte(0x12345678, 2, 0xAB));
	printf("0x%x\n", replace_byte(0x12345678, 0, 0xAB));
	//system("pause");
}

2.61

#include <stdio.h>

int main() {
	int x = 0xFFFFFFFF;
	int y = 0x00000000;
	int z = 0x123456FF;
	int w = 0x00123456;
	/*A*/
	printf("%d %d %d %d\n", !~x, !~y, !~z, !~w);
	/*B*/
	printf("%d %d %d %d\n",  !x, !y, !z, !w);
	/*C*/
	printf("%d %d %d %d\n", !~(x | (-1 << 8)), !~(y | (-1 << 8)), !~(z | (-1 << 8)), !~(w | (-1 << 8)));
	/*D*/
	printf("%d %d %d %d\n", !(x >> ((sizeof(int) - 1) << 3)), !(y >> ((sizeof(int) - 1) << 3)), !(z >> ((sizeof(int) - 1) << 3)), !(w >> ((sizeof(int) - 1) << 3)));
	//system("pause");
}

2.62

#include<stdio.h>
int int_shifts_are_arithmetic() {
	return -2 >> 1 == -1;
}

int main() {
	if (int_shifts_are_arithmetic()) {
		printf("int shifts are arithmetic\n");
	}
	else printf("int shifts are not arithmetic\n");
	//system("pause");
	return 0;
}

2.63

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);
unsigned srl(unsigned x, int k) {
	/*Preform shift arithmetically*/
	unsigned xsra = (int)x >> k;

	int sign = (x & INT_MIN) && (k != 0);                  //符号为负且偏移不为0,则为 sign 为 1,否则为 0
	//xsra -= (unsigned int)(sign * (-1 << (w - k)));    //如果 (int)x 是负则从 xsrl 的前面将 k 个 1 换成 0
	/*题目要求不能用乘法,因此把上式换成*/
	xsra -= (unsigned int)(-sign & (-1 << (w - k)));
	return xsra;
}

int sra(int x, int k) {
	/*Perform shift logically*/
	int xsrl = (unsigned)x >> k;

	int sign = (k != 0) && (x & INT_MIN);  //符号为负且偏移不为0
	//xsrl += sign * (-1 << (w - k));     //如果是负则从 xsrl 的前面将 k 个 0 换成 1
	/*题目要求不能用乘法,因此把上式换成下式*/
	xsrl += (unsigned int)(-sign & (-1 << (w - k)));
	return xsrl;
}
int main() {
	printf("%d\n%d\n%d\n%d\n%d\n", sra(123, 0) == 123 >> 0, sra(-100, 0) == -100 >> 0, sra(-2, w - 1) == -2 >> w - 1, srl(48, 0) == 48 >> 0, srl(UINT_MAX, 2) == UINT_MAX >> 2);
	printf("\nsuccess\n");
	system("pause");
	return 0;
}

2.64

/*Return 1 when any odd bit of x equals 1; 0 otherwise. Assume w=32*/
int any_odd_one(unsigned x) {
	unsigned int t = 0x55555555;
	return (x & t) != 0;
}

2.65

搬来的,完全没有思路

int odd_ones(unsigned x) {
    // 这是第一层的处理,对某一位i而言,通过右移了一位,我们就获取到了i前边的那一位,把他们异或后,
    // 得到的位的值为0或者1,1就表示和前边的一位中有奇数个1,0表示有偶数个1.
    x ^= (x >> 1);

    // 经过上边的处理后呢,x中每一位的值的意义就不同了,他表示该位和它前边的位1的个数是奇数还是偶数
    //  此时我们再右移2位,就获得了i前边的前边的值j,这个值j表示j和前边一位1的个数是奇数还是偶数
    //  异或后,的值就便是到j前边,一共四位1的个数是奇数还是偶数
    x ^= (x >> 2);

    // 后面的都是按照上边的原理依次类推的
    x ^= (x >> 4);
    x ^= (x >> 8);
    x ^= (x >> 16);
    return x & 1;
}

2.66

// 1. 先使用或加位移让第一个1的后边都是1
// 2. 然后取非后右移一位后,最右边的1就是我们想要的掩码
// 3. 由于上边得到的那个1就是原值中的第一个1的位置,因此&上原值就清空了1前边的位
int leftmost_one(unsigned x) {
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;

    return x & (~x >> 1);
}

2.67

A. 位移量大于等于位数, C语言对其行为没有定义

B. C.

#include<stdio.h>
/*int32 和 int16 仅用来测试,实际使用时需用 int*/
__int32 int32_int_size_is_32() {
	__int32 one = 1;
	__int32 set_msb = one << 31;
	__int32 beyond_msb = one << 31;
	beyond_msb <<= 1;
	return set_msb && !beyond_msb;
}

__int16 int16_int_size_is_32() {
	__int16 one = 1;
	__int16 set_msb = one << 15;
	set_msb <<= 15;
	set_msb <<= 1;
	__int16 beyond_msb = one << 15;
	beyond_msb <<= 15;
	beyond_msb <<= 2;
	//printf("%d %d\n", set_msb, beyond_msb);
	return set_msb && !beyond_msb;
}

int main() {
	printf("%d %d\n", int32_int_size_is_32(), int16_int_size_is_32());
	system("pause");
	return 0;
}

2.68

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);		 // int 的位数

int lower_one_mask(int n) {
	int k = w - n;
	return ((UINT_MAX << k) >> k);
}

int main() {
	printf("0x%x 0x%x 0x%x\n",lower_one_mask(6), lower_one_mask(17), lower_one_mask(w));
	system("pause");
	return 0;
}

2.69

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);		 // int 的位数

unsigned rotate_left(unsigned x, int n) { // 0<=n < w
	int t = x << n;
	int s = x >> w - n;
	return t | s;
}

int main() {
	printf("0x%x 0x%x 0x%x\n", rotate_left(0x12345678, 4), rotate_left(0x12345678, 20), rotate_left(0x12345678, 28));
	system("pause");          
	return 0;
}

2.70

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);		 // int 的位数

int fits_bits(int x, int n) {
	int k = w - n;
	int mask = (UINT_MAX << k) >> k;
	int sign = !(x & INT_MIN);					//求 x 的符号
	int nbits_max = mask >> 1;				// n 位补码所能表示的最大值

	//int nbits_min = -nbits_max - 1;							// n 位补码所能表示的最小值
	//return (x >= nbits_min) && (x <= nbits_max);	//不能用比较符号所以改用下式

	return (sign &&							//当 x 为正时
		((x & nbits_max) == x)) ||		
		(!sign &&									//当 x 为负时
		((-x == (nbits_max + 1)) ||		
		((-x & nbits_max) == -x)));
}

int main() {
	printf("%d %d %d\n", fits_bits(2, 2), fits_bits(INT_MIN, 32), fits_bits(-1, 1));
	system("pause");          
	return 0;
}

2.71

A. 得到的结果是unsigned,而并非扩展为signed的结果。
B. 使用int,将待抽取字节左移到最高字节,再右移到最低字节即可。

int xbyte(unsigned word, int bytenum){
    int ret = word << ((3 - bytenum)<<3);
    return ret >> 24;
}

2.72

A. >=语句左边始终是 unsigned,无论怎样都大于等于 0
B. 改为 if(maxbytes > 0 && maxbytes - sizeof(val) >= 0)

2.73

#include<stdio.h>
#include<climits>
int w = 8 * sizeof(int);
int saturating_add(int x, int y) {
	int sum = x + y;
	/*根据书上65页的原理,不过又不能这么“直白”
	if (x > 0 && y > 0 && sum <= 0) {	
		sum = INT_MAX;
	} else if(x < 0 && y < 0 && sum >= 0) {
		sum = INT_MIN;
	}
	*/
	int x_sign = !(x & INT_MIN),													//x的符号
		y_sign = !(y & INT_MIN),														//y的符号
		sum_sign = !(sum & INT_MIN);											//sum的符号
	int pos = x_sign && y_sign && !sum_sign,							//正溢出则 pos 为1
		neg = !x_sign && !y_sign && sum_sign;							//负溢出则 neg 为1
	sum = (sum | -pos) &																//如果正溢出,sum 会变成 INT_MAX,否则不变
		(INT_MAX + (!pos << (w - 1)));		
	sum = (sum & ((!neg << (w - 1)) >> (w - 1))) |						//如果负溢出,sum 会变成 INT_MIN,否则不变
		(neg << (w - 1));		

	return sum;
}
int main() {
	printf("%d   %d   %d  %d\n", INT_MAX + 1, saturating_add(INT_MAX, 1),  1 + 2, saturating_add(1, 2));
	printf("%d   %d   %d  %d\n", INT_MIN - 1, saturating_add(INT_MIN, -1),  -1 + (-2), saturating_add(-1, -2));
	system("pause");
	return 0;
}

2.74

这个题参考了课本习题 2.30,减一个数等于加这个数的相反数,但是 INT_MIN 的相反数溢出,因此分开讨论;
如果 y 不是 INT_MIN,很简单,直接取反;
否则,-y 一定溢出,等于 INT_MAX + 1,如果 x 小于 0,INT_MAX + 1 + x 小于 INT_MAX,一定不溢出;如果 x 大于 0,INT_MAX + 1 + x 一定大于 INT_MAX,一定溢出。

#include<stdio.h>
#include<climits>

int tsub_ok(int x, int y) {
	if (y != INT_MIN) {
		y = -y;

		// 参考习题 2.30
		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;
	}
	//如果 y 是 INT_MIN,-y 一定溢出,等于 INT_MAX + 1
	else {
		// 如果 x 小于 0,INT_MAX + 1 + x 一定小于 INT_MAX,一定不溢出
		if (x < 0) {
			return 1;
		}
		else {
			return 0;
		}
	}
}

int main() {
    // 简单测试
	printf("%d   %d\n", tsub_ok(-1, INT_MIN), tsub_ok(1, INT_MAX));
	printf("%d   %d\n", tsub_ok(INT_MIN, 2), tsub_ok(-1, -2));

	int a = -1;
	__int64 one = a;
	__int64 two = INT_MIN;
	printf("%d %d %d\n", (a - INT_MIN) == (one - two), a - INT_MIN, one - two);
	system("pause");
	return 0;
}

2.75

这个题我不太理解,在 StackOverflow 找到的推导 _ (:з」∠)_

To convert a signed, 2's complement, 32-bit integer to an unsigned 32-bit integer, we add 2³² to its value if it is negative.

signed_high_prod does a signed multiplication and returns bits 63 to 32 of the product. We want unsigned_high_prod to do the same for unsigned multiplication and to make use of signed_high_prod and then compensate for the difference between unsigned and signed multiplication.

Let N(i) = { 1, i < 0
           { 0, i >= 0

Let U(i) = i + N(i)·2³² {2³¹ <= i < 2³¹ }

Then:

U(x)·U(y) = (x + N(x)·2³²)·(y + N(y)·2³²)
          = x·y +N(y)·2³² + N(x)·2³²·y + N(x)·2³²·N(y)·2³²
          = x·y +N(y)·2³² +N(x)·2³² + N(x)·N(y)·2⁶⁴

⌊U(x)·U(y)/2³²⌋ = ⌊x·y/2³²⌋ +N(y) +N(x) + N(x)·N(y)·2³²

Since the arithmetic on unsigned, 32-bit integers will be performed modulo 2³², we have:U(x)·U(y)/2³²⌋ mod 2³²  = (⌊x·y/2³²⌋ +N(y) +N(x) + N(x)·N(y)·2³²) mod 2³²
                         = (⌊x·y/2³²⌋ +N(y) +N(x)) mod 2³²

I believe that accounts for the calculations performed by your unsigned_high_prod function.
下面是我自己写的代码:
#include<stdio.h>
#include <inttypes.h>

/*找不到这样的库函数,下面是我自己编撰的,不能保证完全正确性*/
int signed_high_prod(int x, int y) {
	__int64 X = x, Y = y;
	__int64 s = X * Y;
	//printf("%" PRIx64 " * %" PRIx64 "= %" PRIx64 "\n", X, Y, s);
	s >>= 32;
	return s;
}

/*按照推导写出的函数*/
unsigned unsigned_high_prod(unsigned x, unsigned y) {
	unsigned p = (unsigned)signed_high_prod((int)x, (int)y);
	if ((int)x < 0) {
		p += y;
	}
	if ((int)y < 0) {
		p += x;
	}
	return p;
}

int main() {
	printf("%x   %x\n", unsigned_high_prod(0xFFFFFFFF, 0xFFFFFFFF), signed_high_prod(0xFFFFFFFF, 0xFFFFFFFF));
	system("pause");
	return 0;
}

2.76

void *Calloc(size_t nmemb, size_t size) {
	if (nmemb == 0 || size == 0) return NULL;

	int s = nmemb * size;
	if (s / size == nmemb) {        //检测溢出
		void *p = malloc(s);
		memset(p, 0, s);
		return p;
	}
	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 K = k & (x >> (w - 1));		//如果 x 为负, K 为 k;否则 K 为 0
	x += (1 << K) - 1;					//如果 K 为 0, x 不变;否则进行偏置
	x >>= k;
	return x;
}

2.79

#include<stdio.h>
#include <string.h> 
int w = 8 * sizeof(int);

/* 2.78 中的函数*/
int divide_power2(int x, int k) {
	int K = k & (x >> (w - 1));		//如果 x 为负, K 为 k;否则 K 为 0
	x += (1 << K) - 1;					//如果 K 为 0, x 不变;否则进行偏置
	x >>= k;
	return x;
}

int mul3div4(int x) {
	x = (x << 1) + x;
	return divide_power2(x, 2);
}

int main() {
	int o = 0x12345678, t = -o;
	printf("%d %d\n", mul3div4(o) == 3 * o / 4, mul3div4(t) == 3 * t /4);
	system("pause");
	return 0;
}

2.80

这个题目我不太会,从别的博客搬来的

#include<stdio.h>
#include<climits>
#include<assert.h>
int w = 8 * sizeof(int);
/*
* 这个题目非常有意思,要保证不溢出,就要先做除法,也就是先除以4再乘以3
* 在下边中用到了一个非常巧妙的地方,把一个整数进行拆分
*/

/*
* calculate 3/4x, no overflow, round to zero
*
* no overflow means divide 4 first, then multiple 3, diffrent from 2.79 here
*
* rounding to zero is a little complicated.
* every int x, equals f(first 30 bit number) plus l(last 2 bit number)
*
*   f = x & ~0x3
*   l = x & 0x3
*   x = f + l
*   threeforths(x) = f/4*3 + l*3/4
*
* f doesn't care about round at all, we just care about rounding from l*3/4  //这儿我不太明白,可能是因为 f 加了偏置也没有变化
*
*   lm3 = (l << 1) + l
*
* when x > 0, rounding to zero is easy
*
*   lm3d4 = lm3 >> 2
*
* when x < 0, rounding to zero acts like divide_power2 in 2.78
*
*   bias = 0x3    // (1 << 2) - 1
*   lm3d4 = (lm3 + bias) >> 2
*/

int threeforths(int x) {
	int is_neg = x & INT_MIN;

	int f = x & ~0x3;
	int l = x & 0x3;

	int fd4 = f >> 2;
	int fd4m3 = (fd4 << 1) + fd4;

	int lm3 = (l << 1) + l;
	int bias = (1 << 1) + 1;
	(is_neg && (lm3 += bias));
	int lm3d4 = lm3 >> 2;

	return fd4m3 + lm3d4;
}

int main() {
	assert(threeforths(8) == 6);
	assert(threeforths(9) == 6);
	assert(threeforths(10) == 7);
	assert(threeforths(11) == 8);
	assert(threeforths(12) == 9);

	assert(threeforths(-8) == -6);
	assert(threeforths(-9) == -6);
	assert(threeforths(-10) == -7);
	assert(threeforths(-11) == -8);
	assert(threeforths(-12) == -9);
	system("pause");
	return 0;
}

2.81

#include<stdio.h>
#include<climits>

int main() {
	int k = 5, j = 4;
	int A = (-1 << k), B = ~(-1 << (k + j)) - ((1 << j) - 1);
	printf("A. 0x%x\nB. 0x%x\n", A, B);
	system("pause");
	return 0;
}

2.82

A. x 当 x = 0, y = INT_MIN
B. √ 无论是否溢出,其对低32位的运算没有影响
C. √
~x + ~y + 1 = ~x + 1 + ~y + 1 - 1 = -x + -y - 1 = -(x + y) - 1 = ~(x + y) + 1 - 1 = ~(x + y)
D. √ 无符号数和有符号数位级表示相同
E. √ 左移只能在低位引入0,可能会使 x 变小

2.83

A. 令x为无穷序列表示的值,可以得到x*2^k = Y + x。所以 x = Y/(2 ^k - 1)。
B. (a)5/7, (b)6/15 = 2/5, (c) 19/63

2.84

return ((ux << 1) == (uy << 1)) ||      //都为0 +0 -0
		(!sx && sy) ||                  //x 为负, y 为正
		(sx && sy && (ux <= uy)) ||	    //都为正
		(!sx && !sy && (ux >= uy));	    //都为负

2.85

A.
7.0 = 111.0(二进制); M = 1.11; f = 0.11;
E = 2; Bias = 2^(k - 1) - 1; e = E + Bias = 2 + 2 ^ (k -1) - 1 = 2^(k - 1) + 1;
位表示 = e~f = 10…01 ~ 110…00

B.
e_MAX = 11…110 = 2^k - 2; E_MAX = e_MAX - Bias = 2^(k - 1) - 1;
当 E = n; M = 1.11…; f = 0.11… ;这样才能保证最大而且最低位为 1,即最大奇整数(前提条件是 E_MAX >= n,即指数位数足够大);
此时 e = E + Bias = n + 2^(k - 1) - 1; V = 2^(n+1) - 1;
位表示 = e~f = e ~ 11…11

C.
最小的(正)规格化数: e = 1; E = 1 - 2^(k - 1) + 1 = 2 - 2^(k - 1); M = 1.0; f = 0.0; V = 2^E;
则其倒数:Vr = 2^(-E); Er = -E = 2^(k-1) - 2; Mr = M = 1.0; fr = f = 0.0; er = Er + Bias = 2^k - 3;
位表示 = er~fr = 11…101 ~ 00…00

2.86

便于理解,我加了一列

描述十进制二进制表示
最小的正非规格化数2^(-61-2^14)3.6452e-49510~000…000~0~000…001
最小的正规格化数2^(-2^14+2)3.3621e-49320~000…001~1~000…000
最大的规格化数(2 - 2^(-63)) * 2^(2^14 - 1)1.1897e+49320~111…110~1~111…111

2.87

描述HexMEVD
-00x80000-14-0-0.0
最小的>2的值0x40011025/102411025 * 2^(-9)2.001953
5120x600019512512.0
最大的非规格化的数0x03FF1023/1024-141023 * 2^(-24)0.000061
-∞0xFC00————-∞-∞
十六进制表示为 3BB0 的数3BB0123/64-1123 * 2^(-7)0.960938

2.88

格式A格式B
1 01110 001-9/161 0110 0010-9/16
0 10110 1012080 1110 1010208
1 00111 110-7/2^101 0000 0111-7/2^10
0 00000 1015/2^170 0000 00000
1 11011 000-2^121 1111 0000-∞
0 11000 1007680 1111 0000+∞

2.89

测试代码:

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

int A(int x, double dx) {
	return (float)x == (float)dx;
}

int B(int x, double dx, int y, double dy) {
	return dx - dy == (double)(x - y);
}

int C(double dx, double dy, double dz) {
	return (dx + dy) + dz == dx + (dy + dz);
}

int D(double dx, double dy, double dz) {
	return (dx*dy)*dz == dx * (dy*dz);
}

int E(double dx, double dz) {
	return dx / dx == 1 / 1;
}

int main(int argc, char* argv[]) {
	int x = INT_MAX;
	int y = INT_MAX - 222;
	int z = INT_MAX - 3333333333;
	double dx = (double)x;
	double dy = (double)y;
	double dz = (double)z;

	printf("%d\n", D(dx, dy, dz));
	system("pause");
	return 0;
}

A.√
int转double不会损失;虽然int转float,double转float有可能小数部分损失,但两者仍然相等
B.x
x = INT_MAX,y = -1,int会溢出
C.√
double不会溢出
D.√
x = INT_MAX;
y = INT_MAX - 222;
z = INT_MAX - 3333333333;
double 小数部分可能溢出
E.x
dx = 1;dz = 0
dz/dz = NaN != dx/dx = 1

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<<(x+149);
    } 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);
}

Bias = 127
最小的正非规格化数 = 2^(1-Bias) * 2^(-23) = 2^(-149),因此比 -149 小的都是 0
最大的正规格化数 = 2^(1-Bias) = 2^(-126),因此比 -126 都是非规格化数
最大的规格化数 = 2(28-2 - bias) = 2^127,比 128 小的都是规格化数

2.91

A. 3.141593
B. 根据 2.83,x = Y/(2^k - 1),则 22/7 = 21/7 + 1/7的二进制小数表示为 11.001001001…(y=001)
C. 第一个 π 的二进制小数 11.0010010000111,可以看出是第 9 位

2.92

typedef unsigned float_bits;

int IsNaN(float_bits f) {
	if (((f & 0x7F800000) == 0x7F800000) && ((f & 0x7FFFFF) != 0))
		return 1;
	return 0;
}

float_bits float_negate(const float_bits f) {
	if (IsNaN(f)) {
		return f;
	} else {
		return f ^ 0x80000000;
	}
}

2.93

typedef unsigned float_bits;

int IsNaN(float_bits f) {
	if (((f & 0x7F800000) == 0x7F800000) && ((f & 0x7FFFFF) != 0))
		return 1;
	return 0;
}

float_bits float_absval(float_bits f) {
	if (IsNaN(f)) {
		return f;
	} else {
		return f & 0x7FFFFFFF;
	}
}

2.94

非规格化数 frac 直接左移,尽管可能进位到 exp,值却是正确的;
最大规格化阶数在 exp 加一的同时,frac = 0,使 f 变为 inf;
其他普通的直接 exp 加一;

测试时发现在 f 为 NaN 时会产生难以描述的行为,这就是为什么课本让我们简单的返回 f 的原因吧;
使用了四个线程,速度快很多,i7 大概一分钟就跑完了

#include <stdio.h>
#include<climits>
#include<Windows.h>
#define THREADNUM_MAX 4
typedef unsigned float_bits;

int IsNaN(float_bits f) {
	if (((f & 0x7F800000) == 0x7F800000) && ((f & 0x7FFFFF) != 0))
		return 1;
	return 0;
}

float_bits float_twice(float_bits f) {
	if (IsNaN(f)) {
		return f;
	}
	else {
		unsigned sign = f >> 31;
		unsigned exp = f >> 23 & 0xFF;
		unsigned frac = f & 0x7FFFFF;

		/*非规格化数*/
		if (exp == 0) {
			frac <<= 1;
		}
		/*最大规格化阶数*/
		else if (exp == 0xFE) {
			frac = 0;
			exp = 0xFF;
		}
		/*+∞ -∞*/
		else if (exp == 0xFF) {
			/*do nothing*/
		}
		else {
			exp++;
		}

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

DWORD WINAPI TestFun(LPVOID pM) {
	float_bits start, end;
	/*拆分为 4 个线程*/
	if ((int)pM == 0) {
		start = 0; end = 1073741823;
	}
	else if ((int)pM == 1) {
		start = 1073741824; end = 2147483646;
	}
	else if ((int)pM == 2) {
		start = 2147483647; end = 3221225472;
	}
	else {
		start = 3221225473; end = UINT_MAX;
	}

	union fu
	{
		float f;
		float_bits u;
	} fu1, fu2;

	for (float_bits i = start; i <= end; i++) {
		if (((int)pM == 3) && i == 0) {
			break;
		}
		fu1.u = i; fu2.u = i;

		/*由于NaN运算的复杂性以及题目的要求,对于 NaN 的运算不做处理*/
		if (!IsNaN(i)) {
			fu2.f *= 2;
		}

		if (float_twice(fu1.u) != fu2.u) {
			printf("test%d detected wrong %x\n", (int)pM, i);
			return 0;
		}
	}
	printf("mission%d success!\n", (int)pM);
	return 0;
}

int main() {
	/*创建线程*/
	HANDLE hThread[THREADNUM_MAX];
	for (int i = 0; i < THREADNUM_MAX; i++) {
		hThread[i] = CreateThread(NULL, 0, TestFun, (LPVOID)i, 0, NULL);
	}

	/*等待所有线程结束*/
	WaitForMultipleObjects(THREADNUM_MAX, hThread, TRUE, INFINITE);
	system("pause");
	return 0;
}

2.95

这里我们考虑舍入,比如 f = 0 000…001 XXX…XYZ 进行右移变为 0 000…000 1XX…XXY(Z),题目要求偶数舍入,需要看 Z 位,如果 Z 是 1,则需要舍入。偶数舍入要使 Y 位(最低有效位)为 0,如果 Y 是 1,那么就 f 就要加 1;如果 Y 是 0,直接舍掉 Z。

#include <stdio.h>
#include<climits>
#include<Windows.h>
#define THREADNUM_MAX 4
typedef unsigned float_bits;

int IsNaN(float_bits f) {
	if (((f & 0x7F800000) == 0x7F800000) && ((f & 0x7FFFFF) != 0))
		return 1;
	return 0;
}

float_bits float_half(float_bits f) {
	if (IsNaN(f)) {
		return f;
	}
	else {
		unsigned sign = f >> 31;
		unsigned exp = f >> 23 & 0xFF;
		unsigned frac = f & 0x7FFFFF;
		/*进位*/
		unsigned add = (frac & 1) && ((frac >> 1) & 1);

		/*非规格化数*/
		if (exp == 0) {
			frac >>= 1;
			frac += add;
		}
		/*最小规格化阶数*/
		else if (exp == 0x1) {
			exp--;
			frac = (frac >> 1) + 0x400000;
			frac += add;
		}
		/*+∞ -∞*/
		else if (exp == 0xFF) {
			/*do nothing*/
		}
		/*其他*/
		else {
			exp--;
		}

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

DWORD WINAPI TestFun(LPVOID pM) {
	float_bits start, end;
	/*拆分为 4 个线程*/
	if ((int)pM == 0) {
		start = 0; end = 1073741823;
	}
	else if ((int)pM == 1) {
		start = 1073741824; end = 2147483646;
	}
	else if ((int)pM == 2) {
		start = 2147483647; end = 3221225472;
	}
	else {
		start = 3221225473; end = UINT_MAX;
	}

	union fu
	{
		float f;
		float_bits u;
	} fu1, fu2;

	for (float_bits i = start; i <= end; i++) {
		if (((int)pM == 3) && i == 0) {
			break;
		}
		fu1.u = i; fu2.u = i;

		/*由于NaN运算的复杂性以及题目的要求,对于 NaN 的运算不做处理*/
		if (!IsNaN(i)) {
			fu2.f /= 2;
		}

		if (float_half(fu1.u) != fu2.u) {
			printf("test%d detected wrong %x\n", (int)pM, i);
			return 0;
		}
	}
	printf("mission%d success!\n", (int)pM);
	return 0;
}

int main() {
	/*创建线程*/
	HANDLE hThread[THREADNUM_MAX];
	for (int i = 0; i < THREADNUM_MAX; i++) {
		hThread[i] = CreateThread(NULL, 0, TestFun, (LPVOID)i, 0, NULL);
	}

	/*等待所有线程结束*/
	WaitForMultipleObjects(THREADNUM_MAX, hThread, TRUE, INFINITE);
	system("pause");
	return 0;
}

2.96

比较简单

#include <stdio.h>
#include<climits>
#include<Windows.h>
#define THREADNUM_MAX 4
typedef unsigned float_bits;

int float_f2i(float_bits f) {
	int sign = f >> 31;
	sign = sign ? -1 : 1;
	int exp = f >> 23 & 0xFF;
	int frac = f & 0x7FFFFF;

	int Bias = 127;
	int E = exp - Bias;
	/*F 表示的是小数值乘以 2^23 之后的值*/
	int F = frac + (1 << 23);
	int result;

	/*非规格化数和比1小的数*/
	if (exp == 0 || E < 0) {
		result = 0;
	}
	/*上界*/
	else if (E >= 31) {
		result = 0x80000000;
	}
	/*其他*/
	else {
		if (E - 23 >= 0) {
			result = sign * (int)(F << (E - 23));
		}
		else {
			result = sign * (int)(F >> (23 - E));
		}
	}
	return result;
}

DWORD WINAPI TestFun(LPVOID pM) {
	float_bits start, end;
	/*拆分为 4 个线程*/
	if ((int)pM == 0) {
		start = 0; end = 1073741823;
	}
	else if ((int)pM == 1) {
		start = 1073741824; end = 2147483646;
	}
	else if ((int)pM == 2) {
		start = 2147483647; end = 3221225472;
	}
	else {
		start = 3221225473; end = UINT_MAX;
	}

	union fi {
		float f;
		int i;
	} fi1, fi2;

	for (float_bits i = start; i <= end; i++) {
		if (((int)pM == 3) && i == 0) {
			break;
		}
		fi1.i = i; fi2.i = i;
		fi2.i = (int)fi2.f;

		if (float_f2i(fi1.i) != fi2.i) {
			printf("test%d detected wrong %x %x %x\n", (int)pM, i, float_f2i(fi1.i), fi2.i);
			return 0;
		}
	}
	printf("mission%d success!\n", (int)pM);
	return 0;
}

int main() {
	//创建线程
	HANDLE hThread[THREADNUM_MAX];
	for (int i = 0; i < THREADNUM_MAX; i++) {
		hThread[i] = CreateThread(NULL, 0, TestFun, (LPVOID)i, 0, NULL);
	}

	//等待所有线程结束
	WaitForMultipleObjects(THREADNUM_MAX, hThread, TRUE, INFINITE);
	system("pause");
	return 0;
}

2.97

如 int i = 0 001XX…XX,将其表示为浮点数为 1.XX…XX * 2^Y,所以首先要找到最高位;
单精度浮点数的小数部分长度为23,可能容纳不下,所以需要进行偶数舍入;
另外,如果小数部分 1.11…11 且 Y 为 1,进位变成 10.00…00,这种情况需特殊考虑;
而负数需要 求补 变成正数,方便运算;

#include <stdio.h>
#include<climits>
#include<Windows.h>
#define THREADNUM_MAX 4
typedef unsigned float_bits;

float_bits float_i2f(int i) {
	if (i == 0) return 0;
	//负数需要求补
	int sign = i < 0;
	int ic = sign ? -i : i;
	//找到最高位
	int firstOne = 0;
	for (int j = 31; j >= 0; j--) {
		if ((ic >> j) & 1) {
			firstOne = j;
			break;
		}
	}

	int E = firstOne;
	int Bias = 127;
	//找到需要舍弃的第一个位 firstRound,即连同这个位之后的位都舍弃
	int firstRound = firstOne - 24;
	//小数部分1.XXXX 只保留 XXXX,并将其左移到合适的位置上来
	int frac = (((unsigned)ic << (32 - firstOne)) >> (31 - 22));

	//舍入
	if (firstRound >= 0) {
		//偶数舍入
		if ((ic & ((1 << (firstRound + 1)) - 1)) == (1 << firstRound)) {
			if ((frac & 1) == 1) {
				//特殊处理
				if ((frac == 0x7FFFFF)) {
					E++; frac = 0;
				}
				else {
					frac++;
				}
			}
		}
		//其他情况
		else if ((ic >> firstRound) & 1) {
			//特殊处理
			if ((frac == 0x7FFFFF)) {
				E++; frac = 0;
			}
			else {
				frac++;
			}
		}
	}
	return (sign << 31) | ((E + Bias) << 23) | frac;
}

DWORD WINAPI TestFun(LPVOID pM) {
	float_bits start, end;
	/*拆分为 4 个线程*/
	if ((int)pM == 0) {
		start = 0; end = 1073741823;
	}
	else if ((int)pM == 1) {
		start = 1073741824; end = 2147483646;
	}
	else if ((int)pM == 2) {
		start = 2147483647; end = 3221225472;
	}
	else {
		start = 3221225473; end = UINT_MAX;
	}

	union fi {
		float f;
		int i;
	} fi1, fi2;

	for (float_bits i = start; i <= end; i++) {
		if (((int)pM == 3) && i == 0) {
			break;
		}
		fi1.i = i; fi2.i = i;
		fi2.f = (float)fi2.i;

		if (float_i2f(fi1.i) != fi2.i) {
			printf("test%d detected wrong %x %x %x\n", (int)pM, i, float_i2f(fi1.i), fi2.i);
			return 0;
		}
	}
	printf("mission%d success!\n", (int)pM);
	return 0;
}

int main() {
	//创建线程
	HANDLE hThread[THREADNUM_MAX];
	for (int i = 0; i < THREADNUM_MAX; i++) {
		hThread[i] = CreateThread(NULL, 0, TestFun, (LPVOID)i, 0, NULL);
	}

	//等待所有线程结束
	WaitForMultipleObjects(THREADNUM_MAX, hThread, TRUE, INFINITE);
	system("pause");
	return 0;
}
  • 46
    点赞
  • 191
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CHOOOU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值