文章目录
第二章
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 x∗y + ( x w − 1 x_{w-1} xw−1 y y y + y w − 1 y_{w-1} yw−1 x x x ) 2 w 2^w 2w + x w − 1 x_{w-1} xw−1 y w − 1 y_{w-1} yw−1 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
x∗y) /
2
w
2^w
2w+ (
x
w
−
1
x_{w-1}
xw−1
y
y
y +
y
w
−
1
y_{w-1}
yw−1
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(k−1)−1)=
2
2
−
2
k
−
1
2^{2-2^{k-1}}
22−2k−1
倒数为
2
2
k
−
1
−
2
2^{2^{k-1}-2}
22k−1−2,位表示为0 11…101 00000…
A | B | C | |
---|---|---|---|
阶码 | 2 | n | 2 k − 1 − 2 2^{k-1}-2 2k−1−2 |
尾数 | 1.11 | 1.11…1 | 1.0 |
小数 | 0.11 | 0.11…1 | 0.0 |
值 | 7.0 | 2 n + 1 2^{n+1} 2n+1-1 | 2 2 k − 1 − 2 2^{2^{k-1}-2} 22k−1−2 |
位表示 | 0 1…0 1 11…0 | 0 bias+n 11…1 | 0 1…01 00…0 |
2.86
描述 | 值 | 十进制 |
---|---|---|
最小的正非规格化数 | 2 − 63 ∗ 2 1 − ( 2 14 − 1 ) 2^{-63}*2^{1-(2^{14}-1)} 2−63∗21−(214−1)= 2 − 63 ∗ 2 2 − 2 14 2^{-63}*2^{2-2^{14}} 2−63∗22−214= 2 − 61 − 2 14 2^{-61-2^{14}} 2−61−214 | 3.6452e-4951 |
最小的正规格化数 | 2 1 − ( 2 14 − 1 ) 2^{1-(2^{14}-1)} 21−(214−1)= 2 2 − 2 14 2^{2-2^{14}} 22−214 | 3.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}) 2215−1−(214−1)∗(1+(1−2−63))=2214−1(2−2−63) | 1.1897e+4932 |
2.87
bias= 2 5 − 1 2^{5-1} 25−1-1=15
描述 | Hex | M | E | V | D |
---|---|---|---|---|---|
-0 | 1000 0000 0000 0000=0x8000 | 0 | 0 | -0 | -0.0 |
最小的>2的值 | 0100 0000 0000 0001=0x4001 | 1 + 2 − 10 1+2^{-10} 1+2−10 | 1 | ( 1 + 2 − 10 ) ∗ 2 (1+2^{-10})*2 (1+2−10)∗2 | 2.001953125 |
512 | 0110 0000 0000 0000=0x6000 | 1 | 9 | 512 | 512.0 |
最大非规格化数 | 0000 0011 1111 1111=0x03ff | 1 − 2 − 10 1-2^{-10} 1−2−10 | -14 | ( 1 − 2 − 10 ) ∗ 2 − 14 (1-2^{-10})*2^{-14} (1−2−10)∗2−14 | 6.09755516e-5 |
-oo | 1111 1100 0000 0000=0xfc00 | - | - | -oo | -oo |
0x3BB0 | 0011 1011 1011 0000=0x3bb0 | 123 / 64 123/64 123/64 | -1 | 123 / 128 123/128 123/128 | 0.9609375 |
2.88
A位 | A值 | B位 | B值 |
---|---|---|---|
1 01110 001 | -9/16 | 1 0110 0010 | -9/16 |
0 10110 101 | 13*2^4 | 0 1110 1010 | 13*2^4 |
1 00111 110 | -7/2^10 | 1 0000 0111 | -7/2^10 |
0 00000 101 | 5/2^17 | 0 0000 0001 | 1/2^10 |
1 11011 000 | -2^12 | 1 1110 1111 | -31*2^3 |
0 11000 100 | 3*2^8 | 0 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}
2−9位
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_bits | f | int |
---|---|---|
0 00…00 00…00 — 0 01…11 00…00 | 0 <= f < 1 | 0 |
0 01…11 00…00 — 0 (01…11+31) 0…00 | 1 <= f < 2^31 | 向0舍入 |
0 (01…11+31) 00…00 — 更大 | 2^31 <= f < oo | 0x80000000 |
#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;
}