【CSAPP】家庭作业2.77~2.97
文章目录
- 2.77**
- 2.78**
- 2.79**
- 2.80***
- 2.81**
- 2.82*
- 2.83**
- 2.84*
- 2.85*
- 2.86*
- 2.87*
- 2.88**
- 2.89*
- 2.90*
- 2.91*
- 位级浮点编码规则
- 2.92**
- 2.93**
- 2.94***
- 2.95***
- 2.96****
- 2.97****
2.77**
问:假设我们有一个任务:生成一段代码,将整数变量x
乘以不同的常数因子K
。为了提高效率,我们想只用+
、-
和<<
运算。对于下列K
的值,写出执行乘法运算的C
表达式,每个表达式最多使用3
个运算。
答:
- K = 17
(x << 4) + x;
- K = -7
-(x << 3) + x;
- K = 60
(x << 6) - (x << 2);
- K = -112
-(x << 7) + (x << 4);
测试:
#include <assert.h>void csapp_2_77(int x)
{assert(((x << 4) + x) == x * 17);assert((-(x << 3) + x) == x * -7);assert(((x << 6) - (x << 2)) == x * 60);assert((-(x << 7) + (x << 4)) == x * -112);
}
int main()
{csapp_2_77(0);csapp_2_77(123);csapp_2_77(-123);printf("PASSED!\n");return 0;
}
[liheng@localhost2 2]$ ./a.out
PASSED!
2.78**
问:写出具有如下原型的函数的代码:
int divide_power2(int x, int k);
该函数要用正确的舍入方式计算 x / 2 k x/2^k x/2k。
答:
整数除法:向零取整。
右移除法:向下取整。
因此使用右移计算 x / 2 k x/2^k x/2k时要注意负数。如果x
是负数就需要向上取整,要是它的低k
位是非零的,就需要对右移结果+1
。
int divide_power2(int x, int k)
{int low_k = x & ~(~0 << k);int w = sizeof(int) << 3;int sign = (unsigned)x >> (w - 1);int res = x >> k;(sign && low_k && (res = res + 1));return res;
}
测试:
int main()
{assert(divide_power2(1024, 4) == 1024 / 16);assert(divide_power2(-1024, 4) == -1024 / 16);assert(divide_power2(1027, 4) == 1025 / 16);assert(divide_power2(-1027, 4) == -1025 / 16);printf("PASSED!\n");return 0;
}
[liheng@localhost2 2]$ ./a.out
PASSED!
2.79**
问:写出函数mul3div4
的代码,对于整数参数x
,计算 3 ∗ x / 4 3*x/4 3∗x/4,注意 3 ∗ x 3*x 3∗x会产生溢出。
答:
把x
分为两部分:
- 可以被
4
整除的部分 - 小于
4
的部分。
对于第一部分,先除以4
再乘3
;对第二部分,先乘3
再除以4
,如果x
是负数,要注意向上取整。
#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <limits.h>int mul3div4(int x)
{int h = x & ~0x3;int hdiv4 = h >> 2;int hdiv4mul3 = (hdiv4 << 1) + hdiv4;int l = x & 0x3;int lmul3 = (l << 1) + l;int lmul3div4 = lmul3 >> 2;int sign = x >> ((sizeof(int) << 3) - 1);(sign && (lmul3 & 0x3) && (lmul3div4 = lmul3div4 + 1));return hdiv4mul3 + lmul3div4;
}
测试:
int main()
{assert(mul3div4(0xffff) == (int64_t)0xffff * 3 / 4);assert(mul3div4(0xffffff) == (int64_t)0xffffff * 3 / 4);assert(mul3div4(INT_MAX) == (int64_t)INT_MAX * 3 / 4);assert(mul3div4(INT_MIN) == (int64_t)INT_MIN * 3 / 4);assert(mul3div4(-1) == (int64_t)-1 * 3 / 4);printf("PASSED!\n");return 0;
}
liheng@~/coding/csapp/2$ ./a.out
PASSED!
2.80***
问:写出函数threefourths
的代码,对于整数参数x
,计算 3 / 4 x 3/4x 3/4x的值,向零舍入,它不会溢出。
答:同上。
2.81**
编写C
表达式产生如下位模式,其中 α k \alpha^k αk表示符号 α \alpha α重复 k k k次。假设一个w
位的数据类型,代码可以包含对参数j
和k
的引用,它们分别表示j
和k
的值,但是不能使用表示w
的参数。
- 1 w − k 0 k 1^{w-k}0^k 1w−k0k
~0 << k;
- 0 w − k − j 1 k 0 j 0^{w-k-j}1^{k}0^j 0w−k−j1k0j
(1 << (j + k)) - (1 << j);
2.82*
问:我们在一个int
类型值为32
位的机器上运行程序。这些值以补码形式表示,而且它们都是算术右移的,unsigned
类型的值也是32
位的。
我们产生随机数x
和y
,并且把他们转换成无符号数,显示如下:
int x = random();
int y = random();
unsigned ux = (unsigned)x;
unsigned uy = (unsigned)y;
对于下列每个C
表达式,你要指出表达式是否总是为1
,如果是,请描述其中的原理;如果不是,列举出使它为0
的例子。
答:
- (x < y) == (-x > -y)
否。当x = INT_MIN, y = -1
时,-x
还是INT_MIN
,-y = 1
,明显-x < -y
。 - ((x + y) << 4) + y - x == 17 * y + 15 * x
是。左移4
位相当于乘16
,乘法满足分配律。 - ~x + ~y + 1 == ~(x + y)
是。根据补码运算规则-x = ~x + 1, -y = ~y + 1
得到~x = -x - 1, ~y = -y - 1
,得到:
-x - 1 + (-y) - 1 + 1 = -x - y - 1 = -(x + y) - 1 = ~(x + y)
。 - (ux - uy) == -(unsigned)(y - x)
是。无符号数和补码数有同样的位级行为。 - ((x >> 2) << 2) <= x
是。x
的低2
位可能会变小,低2
位是正权,所以整体值可能会减小。
2.83**
问:一些数字的二进制表示是由形如0.yyy...
的无穷串组成的,其中y
是一个k
位的序列。例如, 1 3 \frac{1}{3} 31的二进制表示是0.01010101...(y=01)
,而 1 5 \frac{1}{5} 51的二进制表示是0.001100110011...(y=0011)
。
答:
- 设 Y = B 2 U k ( y ) Y=B2U_k(y) Y=B2Uk(y),也就是说,这个数具有二进制表示
y
。给出一个由Y
和k
组成的公式表示这个无穷串的值。
定义n = 0.yyy...
,则(n << k) = y.yyy...
,(n << k) - n = Y
,因此 n ∗ ( 2 k − 1 ) = Y n*(2^k-1)=Y n∗(2k−1)=Y,即 n = Y 2 k − 1 n=\frac{Y}{2^k-1} n=2k−1Y - 对于下列的
y
值,串的数值是多少?
- 101
n = 5 2 3 − 1 = 5 7 n=\frac{5}{2^3-1}=\frac{5}{7} n=23−15=75 - 0110
n = 6 2 4 − 1 = 6 15 = 2 5 n=\frac{6}{2^4-1}=\frac{6}{15}=\frac{2}{5} n=24−16=156=52 - 010011
n = 19 2 6 − 1 = 19 63 n=\frac{19}{2^6-1}=\frac{19}{63} n=26−119=6319
2.84*
问:填写下列程序的返回值,这个程序测试它的第一个参数是否小于或者等于第二个参数,假定函数f2u
返回一个无符号32
位数字,其位表示与它的浮点参数相同。你可以假设两个参数都不是NaN
。两种0
,+0
和-0
被认为是相等的。
int float_le(float x, float y)
{unsigned ux = f2u(x);unsigned uy = f2u(y);unsigned sx = ux >> 31;unsigned sy = uy >> 31;return ...;
}
答:
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)) || // +0.0等于-0.0(sx && !sy) || // 负数<正数(sx && sy && (ux >= uy)) || // 负数比较(!sx && !sy && (ux <= uy)); // 正数比较
}
当前机器上,浮点数和整数都是按小端法排列字节的。
测试:
int main()
{printf("%d\n", float_le(+0.0, -0.0));printf("%d\n", float_le(+0.1, -1.0));printf("%d\n", float_le(-0.111, +1.1));printf("%d\n", float_le(+10.0, +10.1));printf("%d\n", float_le(-10.1, -100.01));return 0;
}
liheng@~/coding/csapp/2$ ./a.out
1
0
1
1
0
2.85*
给定一个浮点格式,有k
位指数和n
位小数,对于下列数,写出阶码、尾数码、和值V
的公式。另外,请描述其位表示。
- 数
7.0
。
7.0
可以写成 1.1 1 2 ∗ 2 2 1.11_2 * 2^2 1.112∗22的形式,阶数E=2
,尾数M=1.11
,根据规格化数的浮点编码规则,阶码 e x p r = 2 + b i a s = 2 + 2 k − 1 − 1 expr=2+bias=2+2^{k-1}-1 expr=2+bias=2+2k−1−1,尾数码 f r a c = M − 1 = 0.1 1 2 frac=M-1=0.11_2 frac=M−1=0.112。 - 能够被准确描述的最大奇整数。
最大奇整数的小数位都是1
,并且阶数刚好等于1
的个数,其值为 1.11... 1 2 ∗ 2 n 1.11...1_2*2^n 1.11...12∗2n(n
个小数位1
)。根据规格化数的浮点编码规则,阶码 e x p r = n + b i a s = n + 2 k − 1 − 1 expr=n+bias=n+2^{k-1}-1 expr=n+bias=n+2k−1−1,尾数码 f r a c = M − 1 = 0.111... 1 2 frac=M-1=0.111...1_2 frac=M−1=0.111...12(n
个1
)。 - 最小的规格化数的倒数。
最小的规格化数是 1 ∗ 2 1 − b i a s = 2 1 − 2 k − 1 + 1 1*2^{1-bias}=2^{1-2^{k-1}+1} 1∗21−bias=21−2k−1+1,它的倒数是 2 2 k − 1 − 2 = 2 2 k − 3 − ( 2 k − 1 − 1 ) = 2 2 k − 3 − b i a s 2^{2^{k-1}-2}=2^{2^k-3-{(2^{k-1}-1)}}=2^{2^{k}-3-bias} 22k−1−2=22k−3−(2k−1−1)=22k−3−bias,根据规格化数的浮点编码规则,此时,阶码 e x p r = 2 k − 3 expr=2^k-3 expr=2k−3,尾数码是全0
。
2.86*
问:与Intel
兼容的处理器也支持“扩展精度”浮点形式,这种格式具有80
位字长,被分为1
个符号位,k = 15
个阶码位,1
个单独的整数位和n = 63
个小数位。整数位是IEEE
浮点表示中隐含位的显式副本。也就是说,对于规格化的值它等于1
,对于非规格化的值它等于0
。填写下表,给出用这种格式表示的一些“有趣的”数字的近似值。
答:
描述 | 扩展精度 | |
值 | 十进制 | |
最小的正非规格化数 | 2^{-16445} | 2^{-16445} |
最小的正规格化数 | 2^{-16382} | 2^{-16382} |
最大的规格化数 | (2-2^{-63})*2^16383 | (2-2^{-63})*2^16383 |
2.87*
问:2008
版IEEE
浮点标准,即IEEE 754-2008
,包含了一种16
位的“半精度”浮点格式,它最初是由计算机图形公司设计的,其存储的数据所需的动态范围要高于16
位整数可获得的范围。这种格式具有1
个符号位、k = 5
个阶码位、n = 10
个小数位,阶码偏置量是15
。
对于每个给定的数,填写下表,每一列具有如下指示说明:
Hex
:编码的4
个16
进制数字。
M
:尾数的值,用整数或分数表示。
E
:阶数的值,用整数表示。
V
: V = M ∗ 2 E V=M*2^E V=M∗2E。
D
:可能近似的数值,用printf
的%f
格式打印出来的值。
举个例子, V = 7 8 V=\frac{7}{8} V=87,我们有s = 0
, M = 7 4 M=\frac{7}{4} M=47、 E = − 1 E=-1 E=−1。因此该数的阶码字段是01110
( 14 − b i a s = − 1 14-bias=-1 14−bias=−1),尾数字段是1100000000
,该数编码的16
进制表示是3B00
,其值是0.875
。
答:
描述 |
Hex
|
M
|
E
|
V
|
D
|
---|---|---|---|---|---|
-0 | 8000 | 0 | -14 | 0 ∗ 2 − 14 0*2^{-14} 0∗2−14 | 0.0 |
最小的>2的值 | 4001 | 1025 1024 \frac{1025}{1024} 10241025 | 1 | 1025 1024 ∗ 2 1 \frac{1025}{1024}*2^1 10241025∗21 | 2.001953125 |
512 | 6800 | 1 | 9 | 2 9 2^9 29 | 512.0 |
最大的非规格化数 | 03FF | 1023 1024 \frac{1023}{1024} 10241023 | -14 | 1023 1024 ∗ 2 − 14 \frac{1023}{1024}*2^{-14} 10241023∗2−14 | 0.0000609756 |
− ∞ -\infty −∞ | FC00 | - | - | - ∞ \infty ∞ | - ∞ \infty ∞ |
16进制表示为3BB0的数 | 3BB0 | 123 64 \frac{123}{64} 64123 | -1 | 123 64 ∗ 2 − 1 \frac{123}{64}*2^{-1} 64123∗2−1 | 0.9609375 |
2.88**
问:考虑下面两个基于IEEE
浮点格式的9
位浮点数。
- 格式A
- 有一个符号位
- 有
k = 5
个阶码位,偏置量是15
- 有
n = 3
个小数位
- 格式B
- 有一个符号位
- 有
k = 4
个阶码位,偏置量是7
- 有
n = 4
个小数位
下面给出了一些格式A
表示的位模式,你的任务是把它们转换成最接近的格式B
表示的值。
答:
格式A | 格式B | ||
位 | 值 | 位 | 值 |
1 01110 001 | -9/16 | 1 0110 0010 | -9/16 |
0 10110 101 | 208 | 0 1110 1010 | 208 |
1 00111 110 | -7/1024 | 1 0000 0000 | -0.0 |
0 00000 101 | 5/(2^17) | 0 0000 0000 | 0.0 |
1 11011 000 | -4096 | 1 1111 0000 | -∞ |
0 11000 100 | 768 | 0 1111 0000 | +∞ |
2.89*
问:我们在一个int
类型为32
位补码表示的机器上运行程序。float
类型的值使用32
位IEEE
格式,double
类型的值使用64
位IEEE
格式。我们 产生随机数x
、y
和z
,并把它们转换成double
类型的值。
int x = random();
int y = random();
int z = random();
double dx = (double)x;
double dy = (double)y;
double dz = (double)z;
对于下列的每个C
表达式,请指出是否总为真,如果是,请描述其中的原理;如果不是,列举它为假的例子。
答:
- (float)x == (float)dx
真。dx能精确表示x,把它们都转成float会获取到一样的值。 - dx - dy == (double)(x - y)
假。x - y
可能发生溢出,但dx - dy
不会有溢出。
void csapp_2_89(int x, int y)
{double dx = (double)x;double dy = (double)y;printf("%g %g\n", (double)(x - y), dx - dy);
}int main()
{csapp_2_89(INT32_MIN, 1024); // 负溢出return 0;
}
liheng@~/coding/csapp/2$ ./a.out
2.14748e+09 -2.14748e+09
- (dx + dy) + dz == dx + (dy + dz)
真。double
能准确表示int
,也能准确表示int
的加法运算结果,运算过程中不会舍掉低位,因此满足结合律。 - (dx * dy) * dz = dx * (dy * dz)
假。double
不能精确表示int
与int
的乘积,计算dx * dy
和dy * dz
时都有可能舍掉低有效位,不满足结合律。
void csapp_2_89(int x, int y, int z)
{double dx = (double)x;double dy = (double)y;double dz = (double)z;double d1 = (dx * dy) * dz;double d2 = dx * (dy * dz);printf("%g %g %d\n", d1, d2, d1 == d2);
}int main()
{csapp_2_89(INT32_MAX, INT32_MAX, 123456722);return 0;
}
liheng@~/coding/csapp/2$ ./a.out
5.69344e+26 5.69344e+26 0
- dx/dx == dz/dz
假。如果dx = 0
,结果是NaN
(不等于任何数)。
2.90*
问:编写一个C
函数来计算 2 x 2^x 2x的浮点表示。你意识到完成这个任务的最好方法是直接创建结果的IEEE
单精度表示。当x
太小时,函数返回0.0
;当x
太大时,函数返回+∞
。补充下列代码,假设函数u2f
返回的浮点值与它的无符号参数有相同的位表示。
答:
单精度float
类型的变量有k = 8
个阶码位、n = 23
个小数位,偏置量是127
。
float fpwr2(int x)
{unsigned exp = 0;unsigned frac = 0;if (x < -149) {// 舍入到0exp = 0;frac = 0;} else if (x < -126) {// 非规格化数exp = 0;frac = 1 << (23 - (-126 - x));} else if (x < 128) {// 规格化数exp = x + 127;frac = 0;} else {// 舍入到无穷大exp = 255;frac = 0;}unsigned u = (exp << 23) | frac;return *(float *)&u;
}
测试:
int main()
{assert(fpwr2(-150) == 0.0);assert(fpwr2(-149) == (float)pow(2, -149));assert(fpwr2(-126) == (float)pow(2, -126));assert(fpwr2(127) == (float)pow(2, 127));assert(fpwr2(128) == (float)pow(2, 128));printf("PASSED!\n");return 0;
}
liheng@~/coding/csapp/2$ ./a.out
PASSED!
2.91*
问:大约公元前250
年,希腊数学家阿基米德证明了 223 71 < π < 22 7 \frac{223}{71}<\pi<\frac{22}{7} 71223<π<722。如果当时有一台计算机和标准库math.h
,它就能确定 π \pi π的单精度浮点近似值的十六进制表示为0x40490FDB
。
答:
- 这个浮点值表示的二进制小数是多少?
0x40490FDB
的符号位是0
,阶码位是10000000
,因此阶数是 E = 128 − b i a s = 128 − 127 = 1 E=128 - bias = 128-127=1 E=128−bias=128−127=1;尾数码是100 1001 0000 1111 1101 1011
,尾数是 M = 1 + f = 1 + 2 − 1 + 2 − 4 + 2 − 7 + 2 − 12 + 2 − 13 + 2 − 14 + 2 − 15 + 2 − 16 + 2 − 17 + 2 − 19 + 2 − 20 + 2 − 22 + 2 − 23 = 1.5707963705 M=1+f=1+2^{-1}+2^{-4}+2^{-7}+2^{-12}+2^{-13}+2^{-14}+2^{-15}+2^{-16}+2^{-17}+2^{-19}+2^{-20}+2^{-22}+2^{-23}=1.5707963705 M=1+f=1+2−1+2−4+2−7+2−12+2−13+2−14+2−15+2−16+2−17+2−19+2−20+2−22+2−23=1.5707963705。
M ∗ 2 E = 3.141592741 M*2^E=3.141592741 M∗2E=3.141592741。 - 22 7 \frac{22}{7} 722的二进制小数表示是什么?
22 7 = 3 1 7 \frac{22}{7}=3\frac{1}{7} 722=371,根据家庭作业2.83
, 1 7 = 0. [ 001 ] 2 . . . \frac{1}{7}=0.[001]_2... 71=0.[001]2...,故 22 7 = 11. [ 001 ] 2 . . . \frac{22}{7}=11.[001]_2... 722=11.[001]2... - 这两个 π \pi π的近似值从哪个二进制位开始不同的?
223 71 = 3 10 71 \frac{223}{71}=3\frac{10}{71} 71223=37110, 10 71 = 0.001001000... \frac{10}{71}=0.001001000... 7110=0.001001000...,这两个近似值是在权值为 2 − 9 2^{-9} 2−9的位上开始不同的。
位级浮点编码规则
在接下来的题目中,你所写的代码要实现浮点函数在浮点数的位级表示上直接运算。你的代码应该完全遵循IEEE
浮点运算的规则,包括当需要舍入时,要使用向偶数舍入的方式。
为此,我们把数据类型float_bits
等价于unsigned
。
typedef unsigned float_bits;
你的代码中不使用数据类型float
,而要使用float_bits
。你可以使用数据类型int
和unsigned
,包括无符号和整数常数和运算。你不可以使用任何联合、结构和数组。更重要的是,你不能使用任何浮点数据类型、运算或者常数。取而代之,你的代码应该执行实现这些指定的浮点运算的位操作。
下面的函数说明了对这些规则的使用。对于参数f
,如果f
是非规格化的,该函数返回 ± 0 \pm0 ±0(保持f
的符号),否则返回f
。
float_bits float_denorm_zero(float_bits f)
{unsigned sign = f >> 31;unsigned exp = (f >> 23) & 0xFF;unsigned frac = f & 0x7FFFFF;if (exp == 0) {frac = 0;}return (sign << 31) | (exp << 23) | frac;
}
2.92**
问:
遵循位级浮点编码规则,实现具有如下原型的函数。
float_bits float_negate(float_bits f);
对于浮点数f
,这个函数计算-f
。如果f
是NaN
,你的函数应该简单地返回f
。
答:
float_bits float_negate(float_bits f)
{unsigned sign = f >> 31;unsigned exp = (f >> 23) & 0xFF;unsigned frac = f & 0x7FFFFF;if (!(exp == 255 && frac != 0)) {sign = !sign;}return (sign << 31) | (exp << 23) | frac;
}
测试:
float_bits f2u(float f)
{return *(float_bits *)&f;
}float u2f(float_bits f)
{return *(float *)&f;
}int main()
{assert(u2f(float_negate(f2u(0.0))) == -0.0f);assert(u2f(float_negate(f2u(-0.1))) == 0.1f);assert(u2f(float_negate(f2u(100.0))) == -100.0f);assert(u2f(float_negate(f2u(-3.1415926))) == +3.1415926f);assert(float_negate(UINT_MAX) == UINT_MAX);printf("2.92 PASSED!\n");return 0;
}
小数字面值后面加
f
是告诉编译器把它看作float
,C
语言默认把小数字面值看作double
。
liheng@~/coding/csapp/2$ ./a.out
2.92 PASSED!
2.93**
问:
遵循位级浮点编码规则,实现具有如下原型的函数。
float_bits float_absval(float_bits f);
对于浮点数f
,返回 ∣ f ∣ |f| ∣f∣。如果f
是NaN
,返回f
本身。
答:
float_bits float_absval(float_bits f)
{unsigned sign = f >> 31;unsigned exp = (f >> 23) & 0xFF;unsigned frac = f & 0x7FFFFF;if (!(exp == 255 && frac != 0)) {sign = 0;}return (sign << 31) | (exp << 23) | frac;
}
测试:
int main()
{assert(u2f(float_absval(f2u(0.0))) == 0.0f);assert(u2f(float_absval(f2u(0.1))) == 0.1f);assert(u2f(float_absval(f2u(-100.0))) == 100.0f);assert(u2f(float_absval(f2u(-3.1415926))) == 3.1415926f);assert(float_absval(UINT_MAX) == UINT_MAX);printf("2.93 PASSED!\n");return 0;
}
liheng@~/coding/csapp/2$ ./a.out
2.93 PASSED!
2.94***
问:
遵循位级浮点编码规则,实现具有如下原型的函数。
float_bits float_twice(float_bits f);
对于浮点数f
,返回2.0 * f
。如果f
是NaN
,返回f
。
答:
float_bits float_twice(float_bits f)
{unsigned exp = (f >> 23) & 0xFF;if (exp == 255) { // NaN或无穷大return f;}unsigned sign = f >> 31;unsigned frac = f & 0x7FFFFF;if (exp == 0) { // 非规格化数if ((frac & 0x400000) == 0) {frac <<= 1;} else {exp = 1;frac = (frac << 1) & 0x7FFFFF;}return (sign << 31) | (exp << 23) | frac;}// 规格化数exp += 1;if (exp == 255) {return (sign << 31) | (exp << 23); // 返回无穷大}return (sign << 31) | (exp << 23) | frac;
}
测试:
int main()
{assert(u2f(float_twice(f2u(0.0))) == 0.0f);assert(u2f(float_twice(f2u(0.000001))) == 0.000002f);assert(u2f(float_twice(f2u(-100.0))) == -200.0f);assert(u2f(float_twice(f2u(-3.14))) == -6.28f);assert(float_twice(UINT_MAX) == UINT_MAX);printf("2.94 PASSED!\n");return 0;
}
liheng@~/coding/csapp/2$ ./a.out
2.94 PASSED!
2.95***
问:
遵循位级浮点编码规则,实现具有如下原型的函数。
float_bits float_half(float_bits f);
对于浮点数f
,返回0.5 * f
。如果f
是NaN
,返回f
。
答:
float_bits float_half(float_bits f)
{unsigned exp = (f >> 23) & 0xFF;if (exp == 255) { // NaN或无穷大return f;}unsigned sign = f >> 31;unsigned frac = f & 0x7FFFFF;if (exp == 0) { // 非规格化数frac >>= 1;if (frac & 1) {frac &= ~1; // 向偶数舍入}return (sign << 31) | (exp << 23) | frac;}// 规格化数exp -= 1;if (exp == 0) {frac >>= 1;frac |= 0x400000;if (frac & 1) {frac &= ~1; // 向偶数舍入}}return (sign << 31) | (exp << 23) | frac;
}
测试:
int main()
{assert(u2f(float_half(f2u(0.0))) == 0.0f);assert(u2f(float_half(f2u(0.01))) == 0.005f);assert(u2f(float_half(f2u(-200.0))) == -100.0f);assert(u2f(float_half(f2u(6.28))) == 3.14f);assert(float_half(UINT_MAX) == UINT_MAX);printf("2.95 PASSED!\n");return 0;
}
liheng@~/coding/csapp/2$ ./a.out
2.95 PASSED!
2.96****
问:
遵循位级浮点编码规则,实现具有如下原型的函数。
int float_f2i(float_bits f);
对于浮点数f
,返回(int)f
,向零舍入。如果f
是超出表示范围或者是NaN
,返回0x80000000
。
答:
int float_f2i(float_bits f)
{unsigned exp = (f >> 23) & 0xFF;if (exp == 255) {return 0x80000000;}int e = exp - 127;if (e < 0) {return 0;}unsigned frac = (f & 0x7FFFFF) | 0x800000;int val = 0;if (e <= 23) {val = frac >> (23 - e);} else {val = frac << (e - 23);}unsigned sign = f >> 31;return sign ? -val : val;
}
测试:
int main()
{assert(float_f2i(f2u(0.0)) == 0);assert(float_f2i(f2u(0.1)) == 0);assert(float_f2i(f2u(-0.1)) == 0);assert(float_f2i(f2u(-100.1111)) == -100);assert(float_f2i(f2u(100.1111)) == 100);assert(float_f2i(f2u(768452.5464)) == 768452);assert(float_f2i(f2u(-768452.67687)) == -768452);assert(float_f2i(UINT32_MAX) == 0x80000000);printf("2.96 PASSED!\n");return 0;
}
liheng@~/coding/csapp/2$ ./a.out
2.96 PASSED!
float
最多只能表示24
个有效的二进制数字(加上符号位25
个),所以只能近似表示某些int
(加上符号位32
位有效数字)。
2.97****
问:
遵循位级浮点编码规则,实现具有如下原型的函数。
float_bits float_i2f(int i);
计算(float)i
的位级表示。
答:
float_bits float_i2f(int i)
{unsigned sign = (unsigned)i >> 31;if (sign) {i = ~i + 1;}unsigned valid_bits = 0;while (i >> valid_bits) {++valid_bits;}if (valid_bits == 0) {return sign << 31;}unsigned exp = valid_bits - 1 + 127;if (valid_bits > 24) {i >>= valid_bits - 24;}unsigned frac = i & ~(1 << (valid_bits - 1));frac <<= 23 - (valid_bits - 1);return (sign << 31) | (exp << 23) | frac;
}
测试:
int main()
{assert(u2f(float_i2f(0)) == 0.0f);assert(u2f(float_i2f(1)) == 1.0f);assert(u2f(float_i2f(-45)) == -45.0f);assert(u2f(float_i2f(3456)) == 3456.0f);assert(u2f(float_i2f(-123)) == -123.0f);printf("2.97 PASSED!\n");return 0;
}
liheng@~/coding/csapp/2$ ./a.out
2.97 PASSED!
【CSAPP】家庭作业2.77~2.97相关推荐
- csapp家庭作业第十章
10.6 文件关闭后的描述符也会被删除,故为4 0,1,2均固定为标准输入,标准输出,标准错误,故一般从3开始. 10.7 rio_readnb(rio_t *rp, void *usrbuf, in ...
- #10008. 「一本通 1.1 练习 4」家庭作业
[题目描述] 老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分.每个作业的截止日期和学分可能是不同的.例如如果一个作业学分为 10,要求在 6 天内交,那么要想拿到这 ...
- 信息安全系统设计基础家庭作业
<深入理解计算机系统>家庭作业 * 8.9 答案: 进程对 是否并发 AB 否 AC 是 AD 是 BC 是 BD 是 CD 是 * 8.10 答案: A. 调用一次,返回两次: fork ...
- 一次家庭作业意外搞定40年前的数学猜想,牛津小哥:我只研究了几个礼拜
晓查 萧箫 发自 凹非寺 量子位 | 公众号 QbitAI 只是完成一次普通家庭作业,就把困扰了数学家们几十年的猜想搞出了新花样?! 没错,这是来自牛津大学的Thomas Bloom的亲身经历. 在一 ...
- 20135202闫佳歆-第二章家庭作业-2.69
第二章家庭作业 选题:2.69 分值:三分 作业过程: 以下是rotate_right函数的代码: unsigned rotate_right(unsigned x, int n) {int endb ...
- 很多家长学历不高,无法辅导孩子的家庭作业怎么办?
很多家长学历不高,无法辅导孩子的家庭作业.这种现象过去比较普遍,现在随着国家整体教育水平的提高,家长的学历越来越高,尤其在城市里.这样,家长就具备了知识储备,一些可以胜任对孩子的辅导,他们可以辅导孩子 ...
- 发现孩子做作业用计算机,孩子写作业要用手机完成?家庭作业电子化,到底靠谱不靠谱...
原标题:孩子写作业要用手机完成?家庭作业电子化,到底靠谱不靠谱 "妈妈,拿手机给我,我要开始做作业啦!"最近,有不少家长吐槽,说从开学到现在,孩子几乎每天都有手机上的作业.每天放学 ...
- HIT计算机系统CSAPP大作业
HIT计算机系统CSAPP大作业 摘 要 一.第1章 概述 1.1 Hello简介 ·P2P过程 ·020过程 1.2 环境与工具 1.2.1 硬件环境 1.2.2 软件环境 1.2.3 开发工具 1 ...
- 小学生家庭作业C语言编程,家庭作业
家庭作业一(Chapter 2) P80 2.56 试用不同的示例值来运行show_bytes的代码. 为了能方便地同屏显示多个不同的示例值结果,对原有的代码做了一定的添加修改,使得主函数main中有 ...
最新文章
- 【VB】学生信息管理系统1——系统设计怎样开始?
- c语言获取linux的CPU、内存、IO、磁盘、网速(本机编译通过)
- java出现no XXX in java.library.path的解决办法及eclipse配置
- 爱优腾芒“跑马圈地”,AI广告营销能拯救“盈利难”的视频平台吗?
- OpenGL编程轻松入门之二次几何体
- myeclipse jdk tomcat mysql配置_JDK,TOMCAT,myeclipse,mysql安装以及配置
- C盘空间越来越小怎么办,教你27招
- ASP.NET MVC 5 学习教程:添加查询
- 在文本框中插入@对象
- js 复制图片到剪切板 和 js复制文本到剪切板
- 生活过得很苦 不知道什么时候才能解脱
- Restorator 2007 3.70.1729
- linux内存硬件检测工具下载,极品内存检测工具(Memtest86)
- 用NBSI进行SQL注入***分析及安全解决方案
- Pycharm 快捷键盘
- 专题|从智能家居的“精智生活”谈谈边缘计算的落地生根
- Poco C++库简介
- 嵌入式课程学习 嵌入式硬件工程师需要学习哪些内容?
- 活字印刷引入简单工厂模式
- 苹果手机充电时发生爆炸
热门文章
- Anaconda3 python3.7安装Django稀里糊涂终于successful法
- Hadoop性能调优全面总结
- Python吴恩达深度学习作业13 -- Keras教程
- ESP32_学习笔记(一)NVS的操作(存储和读取大数组)(为什么存入数据成功,读取却为零的原因)
- 在本机搭建FTP服务器
- 2022危险化学品经营单位安全管理人员特种作业证考试题库模拟考试平台操作
- 【wsl2】从头开始配置
- 面试题(2020)微信小程序常见面试题
- android连接和断开蓝牙音箱的问题
- android html footer 固定,HTML5+CSS把footer固定在底部