CASPP 第二章课后习题解答

准备

在实现CSAPP的习题之前,首先需要配置环境。去买一个机器明显不现实,于是我们可以使用docker来搭建我们的学习环境,而且docker非常轻量级,有各种各样的环境可以配置。在接下来的工作中,我们使用vscode+docker进行。推荐七牛云docker镜像

  • docker ubuntu16.04
  • vscode + ssh插件

附带几个常见命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
docker run -it -p 8022:22 --ipc=host --name=ubuntu:16.04 ubuntu /bin/bash 
# 使用清华源
apt update
apt install -y openssh-server

mkdir /var/run/sshd
echo 'root:password' | chpasswd
sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
echo "export VISIBLE=now" >> /etc/profile

service ssh restart

ssh root@localhost -p 8022

docker ps -a
docker commit 容器id csapp

到这里就可以开启vscode进行远程连接docker容器,从而进行代码的编写(记得安装gcc)。

使用Docker File进行构建Docker镜像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 基础镜像
FROM nvidia/cuda:9.0-cudnn7-runtime-ubuntu16.04

# 指定devnet的网络代理才可以访问外网
ENV http_proxy  'http://devnet-proxy.oa.com:8080/'
ENV https_proxy 'http://devnet-proxy.oa.com:8080/'

# 加速ubuntu的软件下载速度
RUN echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse" > /etc/apt/source.list \
    && echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse" >> /etc/apt/source.list \
    && echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse" >> /etc/apt/source.list \
    && echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse" >> /etc/apt/source.list

RUN apt-get update --fix-missing && apt-get install -y curl wget vim net-tools bzip2 git\
  && rm -rf /var/lib/apt/lists/* && mkdir -p /software

WORKDIR /software

RUN wget --quiet https://repo.anaconda.com/archive/Anaconda3-4.2.0-Linux-x86_64.sh -O ~/anaconda.sh && \
    /bin/bash ~/anaconda.sh -b -p /software/conda && \
    rm ~/anaconda.sh && \
    # ln -s /software/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh && \
    echo "export PATH=/software/conda/bin:$PATH" >> ~/.bashrc 
    # && \
    # echo "conda activate base" >> ~/.bashrc

# RUN /software/conda/bin/conda create -n python3.5 python=3.5 pytorch torchvision -c pytorch\
#  && apt clean \
#  && apt autoremove -y

RUN /software/conda/bin/pip install PyHamcrest \
 && /software/conda/bin/pip install --upgrade pip \
 && /software/conda/bin/pip install msgpack \
 && /software/conda/bin/pip3 install torch torchvision \
# && /software/conda/bin/conda install pytorch torchvision -c pytorch \
 && /software/conda/bin/pip install opencv-python \
 && apt clean \
 && apt autoremove -y


CMD [ "/bin/bash" ]

2.55

在你能够访问的不同机器上,使用show_bytes.c来编译并运行示例代码,并确定这些机器的字节顺序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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));
}

void show_float(float x) {
    show_bytes((byte_pointer) &x, sizeof(float)); 
}

void show_pointer(void *x) {
    show_bytes((byte_pointer) &x, sizeof(void *)); 
}

解答: 略。

2.56

试着用不同的示例值来运行show_bytes.c的代码

解答: 略。

2.57

编写程序show_short,show_long,show_double,分别打印为short、long以及double的C语言对象的字节表示。

1
2
3
4
5
6
7
8
9
void show_short(short x) {
    show_bytes((byte_pointer) &x, sizeof(short)); //line:data:show_bytes_amp1
}
void show_long(long x) {
    show_bytes((byte_pointer) &x, sizeof(long)); //line:data:show_bytes_amp1
}
void show_double(double x) {
    show_bytes((byte_pointer) &x, sizeof(double));
}

2.58

编写过程is-little_endian当在小端机器上运行的时候是1,在大端机器上运行是0. 最低有效字节放在前面为小端

1
2
3
4
int is_little_end() {
    int x = 0x01;
    return *((byte_pointer)&x) == 0x01;
}

2.59

编写一个C表达式,它生成一个字,由x的最低有效字节和y中剩下的字节组成,对于运算数x=0x89ABCDEF和y=0x765432,就得到0x765432EF.

1
2
3
4
5
int fun(int x,int y){
    x = x & 0xff;
    y = y & (~0xff);
    return x | y;
}

2.60

假设我们将一个w位的字中从0(最低位)到最高位编号,写出下面的函数,会将指定的字节i换成字节b。

replace(0x12345678, 2, 0xAB) –> 0x12AB5678

replace(0X12345678, 0, 0XAB) –> 0x123456AB

1
2
3
4
5
int replace_byte(int x, int i, int v){
    int mask = 0xff<<(i<<3);
    v = v << (i<<3);
    return (x & ~mask) | v;
}

2.61

写一个C表达式,在下列描述的条件下产生1,而在其他情况下得到0,假设x是int类型。

  • x的任何位都等于1。!~x
  • x的任何位都等于0。!x
  • x的最低有效字节中的位都等于1。 !~(x | ~0xff)(最低有效字节就是低8位)
  • x的最高有效字节中的位都等于0。 !(x>>((sizeof(int)-1)<<3) &0xff)

2.62

编写一个函数int_shifts_are_arithmetic(),在对int类型的数使用算数右移的机器上运行这个函数生成1,而在其他情况生成0。注意这样的事实,-1表达为全1,如果是算数右移,那么不管怎么移,都是-1。

1
2
3
4
5
6
int int_shifts_are_arithmetic()
{
    int num = -1;

    return !(num ^ (num >> 1));
}

2.63

将下面的C函数补充完成。函数srl用算数右移(由值xsra给出)来完成逻辑右移。函数sra用逻辑右移(由值xsrl给出)来完成算数右移。可以通过计算8*sizeof(int)来确定数据类型int中的位数w。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
unsigned srl(unsigned int x, int k)
{
    unsigned xrsa = (int)x >> k;

    int w = sizeof(int) << 3;
    int mask = (int)-1<<(w-k);
    return xrsa & ~mask;
}

int sra(int x, int k) {
  int xsrl = (unsigned) x >> k;
  int w = sizeof(int) << 3;
  int mask = (int) -1 << (w - k);
  // 当第一个为1时候,保持mask不变
  int m = 1 << (w - 1);
  mask &= ! (x & m) - 1;

  return xsrl | mask;
}

2.64

写出代码实现如下函数

1
2
3
4
int any_odd_one(unsigned x)
{
    return !!(0xAAAAAAAA & x);
}

2.65

写出代码实现函数 使用二分法进行对1的处理

1
2
3
4
5
6
7
8
9
int odd_ones(unsigned x)
{
    x ^= x>>16;
    x ^= x>>8;
    x ^= x>>4;
    x ^= x>>2;
    x ^= x>>1;
    return x&0x1;
}

2.66

写出代码实现函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int leftmost_one(unsigned x)
{
    /*
    * first, generate a mask that all bits after leftmost one are one
    * e.g. 0xFF00 -> 0xFFFF, and 0x6000 -> 0x7FFF
    * If x = 0, get 0
    */
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;

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

2.67

给你一个任务,编写一个过程int_size_is_32(),当在一个int是32位机器上运行的时候产生1,而在其他情况下产生0。不允许用sizeof运算符。

1
2
3
4
5
6
7
int bad_int_size_is_32()
{
    int set_msb = 1<<31;
    int beyond_msb = 1<<32;

    return set_msb && !beyond_msb;
}

A. 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 it undefined.

B. 修改代码,使得它在至少为32位机器上能够运行

1
2
3
4
5
6
int int_size_is_32()
{
    int set_msb = 1<<31;
    int beyond_msb = set_msb<<1;
    return set_msb && !beyond_msb;
}

C. 修改代码,使得它在至少为16位机器上能够运行

1
2
3
4
5
6
int int_size_is_32()
{
    int set_msb = 1 << 15 << 15 << 1;
    int beyond_msb = set_msb<<1;
    return set_msb && !beyond_msb;
}

2.68

写出代码实现函数

1
2
3
4
5
int lower_one_mask(int n)
{
    int w = sizeof(int) << 3;
    return (unsigned)-1 >> (w-n);
}

2.69

写出具有如下原型的函数代码

1
2
3
4
5
unsigned rotate_left(unsigned x, int n)
{
    int w = 32;
    return (x<<n) + (x>>(w-n));
}

2.70

写出具有如下原型的函数代码

1
2
3
4
5
6
7
8
9
int fits_bits(int x, int n)
{
    int pos_max = (1<<(n-1)) - 1;
    int neg_max = -(1<<(n-1));

    printf("%d %d\n", pos_max, neg_max);

    return x<=pos_max && x>=neg_max;
}

在这里,书上第二章45页说到,当有w位补码(最高位表示为符号位)所能表示的范围是 $$ TMin_w=-2^{w-1},TMax=2^{w-1}-1 $$

2.71

实现一组过程来实现操作一个数据结构,要将4个有符号字节封装成一个32位unsigned。一个字中字节从0编号到3.分配给你的任务是:为一个使用补码运算和算术右移的机器编写下面的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int xbyte(packet_t word, int bytenum) {
  /*
   * pay attention when byte we want is negetive
   *
   * Assume sizeof(unsigned) is 4
   * first shift left 8 * (4 - 1 - bytenum)
   * then arthemetic shift right 8 * (4 - 1) reserve signficant bit
   */
  int size = sizeof(unsigned);
  int shift_left_val = (size - 1 - bytenum) << 3;
  int shift_right_val = (size - 1) << 3;
  return (int) word << shift_left_val >> shift_right_val;
}

2.72

给你一个任务写出一个函数,将整数val复制到缓冲区buf中,但是只有当缓冲区中有足够的可用空间时,才执行复制。

1
2
3
4
5
6
7
void copy_int(int val, void *buf, int maxbytes)
{
    /* compare two signed number, avoid someone set maxbytes a negetive value */
    if(maxbytes>=(int)sizeof(val)){
        memcpy(buf, (void*)&val, sizeof(val));
    }
}

A. 由于sizeof是size_t类型,那么相减以后仍然是该类型>0永远是存在的,因此使用直接比较的方法更好

1
printf("%zu", 0 - sizeof(int)); // 4294967292

B. 如上

2.73

写出具有如下原型的函数代码 与传统的溢出相反。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int saturating_add(int x, int y)
{
    int sum = x + y;
    int sig_mask = INT_MIN;
    /* x>0, y>0, sum<0 pos_overflow
    *  x<0, y<0, sum>0 neg_overflow
    */ 
    int pos_over = !(x & sig_mask) && !(y & sig_mask) && (sum & sig_mask);
    int neg_over = (x & sig_mask) && (y & sig_mask) && !(sum & sig_mask);

    (pos_over && (sum=INT_MAX)) || (neg_over && (sum=INT_MIN));

    return sum;
}

2.74

写出具有如下原型的函数代码(如果计算x-y不溢出,这个函数就返回1)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int tsub_ok(int x, int y)
{   
    int sub = x - y;
    int pos_over = x > 0 && y < 0 && sub < 0;
    int neg_over = x < 0 && y > 0 && sub > 0;

    int res = !(pos_over || neg_over);

    return res;
}

2.75

计算x*y的完整2w位表示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
int signed_high_prod(int x, int y) {
  int64_t mul = (int64_t) x * y;
  return mul >> 32;
}

unsigned unsigned_high_prod(unsigned x, unsigned y) {
  int sig_x = x >> 31;
  int sig_y = y >> 31;
  int signed_prod = signed_high_prod(x, y);
  return signed_prod + x * sig_y + y * sig_x;
}

unsigned another_unsigned_high_prod(unsigned x, unsigned y) {
  uint64_t mul = (uint64_t) x * y;
  return mul >> 32;
}

2.76

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void *another_calloc(size_t nmemb, size_t size)
{
    if (nmemb == 0 || size == 0) { 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; 
}

2.77

为了提高计算效率,乘以不同的因子K,使用+-<<来实现。

  • K=17 (x<<3)+x
  • K=-7 (x-(x<<3))
  • K=60 (x<<6)-(x<<2)
  • K=-112 (x<<4)-(x<<7)

2.78

写出具有如下原型的函数代码

1
2
3
4
5
6
7
8
int divide_power2(int x, int k)
{
    int is_neg = x & INT_MIN;
    //加上偏量 CSAPP73页
    (is_neg && (x=x + (1 << k) - 1));   
    printf("%d\n", x);
    return x >> k;
}

2.79

1
2
3
4
int mul3div4(int x)
{
    return x - ((x+(1<<2)-1)>>2);
}

2.80

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int threeforths(int x)
{
    int f = x & ~0x3;
    int l = x & 0x3;

    f = ((f<<1)+f)>>2; // 3f/4

    int lm3 = (l<<1)+l;
    int bias = (1 << 2) - 1;

    int is_neg = x & INT_MIN;

    (is_neg && (lm3 += bias));

    int lm3d4 = lm3 >> 2;

    return f + lm3d4;

}

2.81

A. $1^{w-k}0^k$

-1 << k

B. $0^{w-k-j}1^k0^j$

~(-1<<k)<<j;

2.82

我们在一个int类型值为32的机器上运行程序,这些值以补码形式表示,而且他们都是算数右移的。unsigned类型也是32位的。

1
2
3
4
5
x = random()
y = random()

ux = (unsigned)x
uy = (unsigned)y

对于下列的每一个C表达式,你要指出是否为1

A. (x<y)==(-x>-y) false(当x为INT_MIN时候, -x==x)

B. ((x+y)<<4)+y-x==17*y+15x true

C. ~x+~y+1==~(x+y)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
true
/*
 * right
 *
 * ~x + ~y + 1
 *   =>
 * ~x + 1 + ~y + 1 - 1
 *   =>
 * -x + -y - 1
 *   =>
 * -(x + y) - 1
 *   =>
 * ~(x + y) + 1 - 1
 *   =>
 * ~(x + y)
 */

D. (ux-uy)==-(unsigned)(y-x) true

E. ((x>>2)<<2)<=x false

2.83

一些数字的二进制表示由形如0.yyyyyyyy无穷串组成,其中y是一个k位的序列。

A.

n = 0.yyyyy...

n << k = y.yyyyy... = Y + n

n << k - n = Y

n = Y/(2^k - 1)

B.

  • $\frac{5}{7}$
  • $\frac{2}{5}$
  • $\frac{19}{63}$

2.84

填写下列程序的返回值。(需要理解float在计算机中的表示形式)

浮点数5在计算中的表示为40A00000,也就是0100 0000 1010 0000 0000 0000 0000 0000,那么如何变成我们想要的5.(s=1,exp=8,frac=23)

  1. Bias=127
  2. E = e-Bias = $2^7$ + $2^0$ - 127 = 2
  3. $f = 0.01 = \frac{1}{4}$
  4. $M = 1+f = \frac{5}{4}$
  5. $V = (-1)^0 * M * 2^E = 5$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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 == 0 && uy << 1 == 0) ||
    (sx && !sy) ||   /* x < 0, y >= 0 or x <= 0, y > 0 */
    (!sx && !sy && ux <= uy) ||/* x > 0, y >= 0 or x >= 0, y > 0 */
    (sx && sy && ux >= uy);   /* x < 0, y <= 0 or x <= 0, y < 0 */

}

2.85

给定一个浮点格式,有k位指数和n位小数,对于下列数,写出阶码E、位数M、小数f和值V的公式。

A. 数7.0

1
0 10....01 110....

B. 能够被准确描述的最大奇整数

1
0 bias+n 11111....

C. 最小的规格化数的倒数。

1
0 11...101 00000.....

2.86

bias = 2^(15-1) - 1

description binary decimal
least positive unstandard 0 0000…(15) 0 000…(62)1 2^(1-bias-63)
least positive standard 0 000…(14)1 1 000….(63) 2^(1-bias)
bigest standard 0 111…(14)0 1 111…(63) 2^bias * (2-2^-63)

2.87

Desc Hex M E V D
-0 0x8000 0 -14 -0 -0.0
>2 least 0x4001 1025/1024 1 1025/512 2.00195312
512 0x6000 1 9 512 512.0
bigest denormalized 0x03FF 1023/1024 -14 1023/(2^24) 6.09755516e-5
-∞ 0xFC00 - - -∞ -∞
ox3BB0 0x3BB0 123/64 -1 123/128 0.9609375

2.88

A bit A value B bit B value
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^11 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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int A(int x, double dx) {
  return (float)x == (float)dx;
}

/* wrong when y is INT_MIN */
int B(int x, double dx, int y, double dy) {
  return dx-dy == (double)(x-y);
}

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

/*
 * wrong
 *
 * FIXME I don't know what conditions cause false
 */
int D(double dx, double dy, double dz) {
  return (dx*dy)*dz == dx*(dy*dz);
}

/* wrong when dx != 0 and dz == 0 */
int E(double dx, double dz) {
  return dx/dx == dz/dz;
}

2.90

分配给你一个任务,编写一个C函数来计算$2^x$的浮点数表示。假设u2f返回的是浮点数值与它的无符号参数有相同的位表示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
float fpwr2(int x)
{
    unsigned exp, frac;
    unsigned u;
    if(x < 2-pow(2,7)-23)
    {
        // to small
        exp = 0;
        frac = 0;
    }else if (x < 2-pow(2,7))
    {
        exp = 0;
        frac = 1 << (unsigned)(x - (2-pow(2,7)-23));
    }else if (x < pow(2,7)-1+1)
    {
        exp = pow(2,7)-1+x;
        frac = 0;
    }

    else
    {
        /* too big*/
        exp = 0xff;
        frac = 0;
    }
    
    u = exp<<23 | frac;
    return *(float*)&u;
}

2.91

大约公元前250年,希腊数学家阿基米德证明了$\frac{233}{71} < \pi < \frac{22}{7}$,$\pi$的十六进制表示为0x40490fdb

A. 这个浮点数二进制为多少? 0 10000000 10010010000111111011011

B. $\frac{22}{7}$ 二进制小数是什么? 1.001001(001)...

C. 从第9位开始不同

2.92

对于浮点数f计算-f,只允许用位级浮点编码规则。

将exp以及frac以及sign提取出来,然后对sign进行操作即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
typedef unsigned float_bits;

unsigned f2u(float f) {
  return *(unsigned*) &f;
}

float u2f(unsigned u) {
  return *(float*) &u;
}

float_bits float_negate(float_bits f) 
{
    unsigned sig = f >> 31;
    unsigned exp = f >> 23 & 0xFF;
    unsigned frac = f & 0x7FFFFF;
    int is_NAN = (exp == 0xFF) && (frac != 0);

    if(is_NAN) return f;
    return ~sig <<31 | exp<<23 | frac; 
}

2.93

对于浮点数f,计算f的绝对值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
float_bits float_absval(float_bits f)
{
    unsigned sig = f >> 31;
    unsigned exp = f >> 23 & 0xFF;
    unsigned frac = f & 0x7FFFFF;
    int is_NAN = (exp == 0xFF) && (frac != 0);
    if(is_NAN) return f;

    if (sig & 0x1)
        return  ~sig <<31 | exp<<23 | frac;
    else
    {
        return  sig <<31 | exp<<23 | frac;
    }
}

2.94

计算2*f。(对指数进行+1即可)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
float_bits float_twice(float_bits f)
{
    unsigned sig = f >> 31;
    unsigned exp = f >> 23 & 0xFF;
    unsigned frac = f & 0x7FFFFF;
    int is_NAN = (exp == 0xFF) && (frac != 0);
    if(is_NAN) return f;

    if(exp==0)
    {
        frac <<= 1;
    }else if (exp == 0xFF - 1)
    {
        exp = 0xFF;
        frac = 0;
    }
    else
    {
        exp += 1;
    }

    return sig <<31 | exp<<23 | frac;
}

2.95

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
float_bits float_half(float_bits f)
{
    unsigned sig = f >> 31;
    unsigned exp = f >> 23 & 0xFF;
    unsigned frac = f & 0x7FFFFF;
    int is_NAN = (exp == 0xFF) && (frac != 0);
    if(is_NAN) return f;

    if(exp==0)
    {
        frac >>= 1;
    }else if (exp == 0xFF - 1)
    {
        exp = 0xFF;
        frac = 0;
    }
    else
    {
        exp -= 1;
    }

    return sig <<31 | exp<<23 | frac;
}

2.96

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int float_f2i(float_bits f)
{
    unsigned sig = 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) 
    {
        num = 0;
    }
    else if (exp >= 31 + bias) 
    {
        num = 0x80000000;
    }
    else 
    {
        E = exp - bias;
        M = frac | 0x800000;
        if (E > 23) 
        {
            num = M << (E - 23);
        }
        else
        {
            num = M >> (23 - E);
        }
        
    }

    return sig?-num:num;
}

2.97

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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;
}

/*
 * generate mask
 * 00000...(32-l) 11111....(l)
 *
 * e.g.
 * 3  => 0x00000007
 * 16 => 0x0000FFFF
 */
unsigned bits_mask(int l) {
  return (unsigned) -1 >> (32-l);
}

float_bits float_i2f(int i) {
  unsigned sig, exp, frac, rest, exp_sig /* except sig */, round_part;
  unsigned bits, fbits;
  unsigned bias = 0x7F;

  if (i == 0) {
    sig = 0;
    exp = 0;
    frac = 0;
    return sig << 31 | exp << 23 | frac;
  }
  if (i == INT_MIN) {
    sig = 1;
    exp = bias + 31;
    frac = 0;
    return sig << 31 | exp << 23 | frac;
  }

  sig = 0;
  /* 2's complatation */
  if (i < 0) {
    sig = 1;
    i = -i;
  }

  bits = bits_length(i);
  fbits = bits - 1;
  exp = bias + fbits;

  rest = i & bits_mask(fbits);
  if (fbits <= 23) {
    frac = rest << (23 - fbits);
    exp_sig = exp << 23 | frac;
  } else {
    int offset = fbits - 23;
    int round_mid = 1 << (offset - 1);

    round_part = rest & bits_mask(offset);
    frac = rest >> offset;
    exp_sig = exp << 23 | frac;

    /* round to even */
    if (round_part < round_mid) {
      /* nothing */
    } else if (round_part > round_mid) {
      exp_sig += 1;
    } else {
      /* round_part == round_mid */
      if ((frac & 0x1) == 1) {
        /* round to even */
        exp_sig += 1;
      }
    }
  }

  return sig << 31 | exp_sig;
}

参考