我自己写的,代码均经过简单测试,如果有什么不对的地方,敬请留下评论
第二章
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
2.66
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.73
#include<stdio.h>
int tsub_ok(int x, int y) {
int Y = -y;
int sum = x + Y;
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
return !pos && !neg;
}
int main() {
printf("%d %d\n", tsub_ok(INT_MAX, -1), tsub_ok(1, 2));
printf("%d %d\n", tsub_ok(INT_MIN, 1), tsub_ok(-1, -2));
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 + x·N(y)·2³² + N(x)·2³²·y + N(x)·2³²·N(y)·2³²
= x·y + x·N(y)·2³² + y·N(x)·2³² + N(x)·N(y)·2⁶⁴
⌊U(x)·U(y)/2³²⌋ = ⌊x·y/2³²⌋ + x·N(y) + 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³²⌋ + x·N(y) + y·N(x) + N(x)·N(y)·2³²) mod 2³²
= (⌊x·y/2³²⌋ + x·N(y) + 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-4951 | 0\~000…000\~0\~000…001 |
最小的正规格化数 | 2\^(-2\^14+2) | 3.3621e-4932 | 0\~000…001\~1\~000…000 |
最大的规格化数 | (2 - 2\^(-63)) * 2\^(2\^14 - 1) | 1.1897e+4932 | 0\~111…110\~1~111..111 |
2.87
描述 | Hex | M | E | V | D |
---|---|---|---|---|---|
-0 | 0x8000 | 0 | -14 | -0 | -0.0 |
最小的>2的值 | 0x4001 | 1025/1024 | 1 | 1025 * 2^(-9) | 2.001953 |
512 | 0x6000 | 1 | 9 | 512 | 512.0 |
最大的非规格化的数 | 0x03FF | 1023/1024 | -14 | 1023 * 2^(-24) | 0.000061 |
-∞ | 0xFC00 | —— | —— | -∞ | -∞ |
十六进制表示为 3BB0 的数 | 3BB0 | 123/64 | -1 | 123 * 2^(-7) | 0.960938 |