练习题:CSAPP Chapter2 Homework(已完结)

Homework

1
2
log:
2022-2-21 对2.32和2.74进行了修正

代码在2.74处

正溢出一定为负,负溢出可能为0或正

截屏2022-02-06 下午10.06.26

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-07%20%E4%B8%8B%E5%8D%886.53.48.png

关于x+y是否溢出的一些说明(我的好朋友给我写了这个hhh 感觉写的很对很清晰):

截屏2022-02-21 下午8.55.12 截屏2022-02-21 下午8.33.43

所以有2.32的正式解题思路:

截屏2022-02-21 下午8.56.13

在解题前,先推导一个小知识点:

(无论是unsigned 的x、y 还是two’s complement的x、y)w位的x和w位的y相乘结果最多要2w位表示。

证明如下:

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-06%20%E4%B8%8B%E5%8D%8811.03.26-20220209164304982.png

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-06%20%E4%B8%8B%E5%8D%8811.02.40-20220212000736427.png

2.35解题思路:

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-06%20%E4%B8%8B%E5%8D%8810.55.16-20220209164322273.png

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-06%20%E4%B8%8B%E5%8D%8810.55.33.png

如果有溢出,t一定不为0;那么p除x就一定不等于0。看第三问就是站在可能溢出的乘法上推导的。

1
2
3
4
5
6
7
//如果计算x*y不溢出,这个函数就返回1
int tmult_ok(int x, int y)
{
    int p = x*y
      //如果x为0就一定不会溢出 如果x不为0 y为0则p一定为0第二个等式也成立。
    return !x||p/x ==y;
}

x的任何位都为1:

方法1:

1
!~x

方法2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
x&=x>>16;

x&=x>>8;

x&=x>>4;

x&=x>>2;

x&=x>>1;

return x&0x1; 

x的任何位都为0:

方法1:

1
return !x;

方法2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
x|=x>>16;

x|=x>>8;

x|=x>>4;

x|=x>>2;

x|=x>>1;

return x&0x1;

x的最低有效字节中的位都等于1

方法1:

1
2
x = x|(-1<<8);
is_allOne(x);

方法2:

1
2
3
4
5
6
7
x&=x>>4;

x&=x>>2;

x&=x>>1;

return x&(0x1); 

x的最高有效字节中的位都等于0

方法1:

1
2
3
 //0xff000000
    x = x&(-1<<((sizeof(int)-1)<<3));
    is_allZero(x);

方法2:

1
2
3
4
5
6
7
x&=x>>4;

x&=x>>2;

x&=x>>1;

return x&(1<<(sizeof(int)<<3-8)); //等价于x&0000000100...(w-1个0) 

使用算术右移来代替逻辑右移

  1. ~((k!=0)-1))的作用(相当于if的作用)

当k为0时,上式为0x0和任何stuff(((int)-1)«((sizeof(int)«3)-k)))相与都为-0

当k为1时,上式为0xffffffff,保证了和前面相与结果不会产生影响。

2.((int)-1)«((sizeof(int)«3)-k))

当0<k<32时,相当于0xFFFFFFFF向左移动了32-k位(0x1…100000.. k个1 32-k个0),此时和~((k!=0)-1))相与结果不变。

当k=32时,此时的c程序并没有保证说结果应该是什么,所以排出这种情况。

当k=0时,相当于0xFFFFFFFF向左移动了32位,结果是不定的,在c语言里规避了对它的解释,和~((k!=0)-1))相与后为0,再取补为0xffffffff,再和xsra相与后为原来的值。

总结:巧妙的使用k!=0来对运算,解决32位移动带来的不确定性问题。

1
2
3
4
5
6
//shift right logical
unsigned srl(unsigned x, int k)
{
    unsigned xsra = (int)x>>k;
    return xsra&(~(((((int)-1)<<((sizeof(int)<<3)-k)))&(~((k!=0)-1))));
}

使用逻辑右移来实现算术右移

  1. ~((k!=0)-1))的作用(相当于if的作用)

当k为0时,上式为0x0保证了和前面相与为0

当k为1时,上式为0xffffffff,保证了和前面相与结果不会产生影响。

  1. int sign = !(x&(1«(sizeof(int)«3)-1))

    (1«(sizeof(int)«3)-1)为10000..0

    如果符号位(w-1位)为1,则x与之相与为1000000…. sign为0 (sign-1)为-1(0xfffff…)

    如果符号为为0,则与之相与为0000000…. sign为1 (sign-1)为0(0x0)

    3.(~0)«((sizeof(int)«3)-k))&(sign-1)

    如果符号位为1,则上式(~0)«((sizeof(int)«3)-k))

    如果符号位为0,则上式为0

1
2
3
4
5
6
7
int sra(int x, int k)
{
    //逻辑右移
    int xsrl = (unsigned) x>>k;
    int sign = !(x&(1<<(sizeof(int)<<3)-1));
    return xsrl|((((~0)<<((sizeof(int)<<3)-k))&(sign-1))&(~((k!=0)-1)));
}

//如果x中的任何奇数位为1 就返回1

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

使用以下算法:

1.点圆为任意位级运算

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-01%20%E4%B8%8B%E5%8D%881.45.31.png

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;
}

这样就可以保证最高位出现1之后,所有的位都置为1.

如0101 –> 0111

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int leftmost_one(unsigned x)
{
    x|=x>>16;
    x|=x>>8;
    x|=x>>4;
    x|=x>>2;
    x|=x>>1;
    //(x&&1)保证x为0的时候返回0
    return (x>>1) + (x&&1);
}

当int左or右移动的位数大于等于sizeof(int)«8时,c语言里没有定义这种情况时应该产生什么样的结果。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int int_size_is_32()
{
    //将第31(0到31位)位设置为1
    int set_msb = 1<<15<<15<<1;

    int beyond_msb = 1<<15<<15<<1<<1;

    return set_msb&&(!beyond_msb);

}
1
2
3
4
5
6
7
8
int lower_one_mask(int n)
{
    //~((n==sizeof(int)<<3) -1)的作用是用来处理n=32的情况
    return  ((1<<n) - 1 )|(~((n==sizeof(int)<<3) -1));
}

//另一种处理方法 很巧妙
return (0x10<<(n-1) -1)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
unsigned rotate_left(unsigned x, int n)
{
    //将高n位设置为1 其余位设置为0
    unsigned t = x&((~0)<<((sizeof(int)<<3) - n));
        
    //将高n位的1移动至低n位
    t = t>>((sizeof(int)<<3) - n);

    //将x左移n位
    x = x<<n;
    //printf("%x\n",x);

   //进行合并
    x|=t;
    printf("%x\n",x);

}
1
2
3
4
5
6
7
8
//判断x能否用n位2's complement表示 -- 考察的是补码扩展的规则
int fits_bits(int x, int n)
{
    //查看w-1位到n-1位是否为全0 或者全1(补码扩展的应用)只有这种情况的补码才可以缩短
    x = x>>(n-1);
    //~x为了保证x为全1 而不是 0x110000...等的不为0的值
   return !x || !~x ;
}
1
2
3
4
5
6
7
8
9
int xbyte(packet_t word, int bytenum)
{
    //先将要抽取的字节移动到最高位 作用:使用算术右移
    int t = (int)word << ((3 - bytenum)<<3);
    printf("%.8x\n",t);
        t = t>>24;
    printf("%.8x\n",t);

}
1
2
3
4
5
6
7
8
9
void copy_int(int val, void *buf, int maxbytes)
{
    //maxbytes - (int) sizeof(val) >= 0 也可以 因为一个正加负一定不会溢出
    if (maxbytes >= (int) sizeof(val))
    {
        //将val复制到buf
        memcpy(buf,&val, sizeof(val));
    }
}

2.26中:

strlen为unsigned int类型

strlen(s) - strlen(t)>0

减strlen(t)相当于无符号数求反 如果strlen(t)不为0 则- strlen(t) == 2^w - strlen(t) (左边为机器运算 右边为人行为运算)

所以strlen(s) - strlen(t) == strlen(s) +2^w - strlen(t) (左边为机器运算 右边为人行为运算)

如果上式发生溢出(右边的 strlen(s) +2^w - strlen(t)中 strlen(s) - strlen(t) 大于0 结果减 2^w 对计算没影响

如果上式发生不溢出(右边的 strlen(s) +2^w - strlen(t)中 strlen(s) - strlen(t) 小于0 结果减 2^w减一个负数很可能变成一个比strlen(s) 和 strlen(t)还大的数。

 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 saturating_add(int x, int y)
{
    //正溢出时返回Tmax 负溢出时返回Tmin
    //x_sign_mask、y_sign_mask为符号位的掩码,结果为0xffffffff(表示x为负数) or 0x0(表示x为正数)
    int x_sign_mask = x>>((sizeof(int)<<3)-1);
    int y_sign_mask = y>>((sizeof(int)<<3)-1);

    int sum = x + y;
    //若sum_sign_mask为0xffffffff 表示sum为负数
    int sum_sign_mask = sum >> ((sizeof(int)<<3)-1);

    //写出正溢出的标志
    //正溢出时~x_sign_mask为0xffffffff、~y_sign_mask为0xffffffff、 sum_sign_mask为oxffffffff
    //否则无论何种情况 下面式子都为0x0
    int postive_flag = ~x_sign_mask & ~y_sign_mask &sum_sign_mask;

    //负溢出时x_sign_mask为0xffffffff、y_sign_mask为0xffffffff、 ~sum_sign_mask为0xffffffff
    //否则无论何种情况 下面式子都为0x0
    int negative_flag = x_sign_mask & y_sign_mask & ~sum_sign_mask;

    //若正溢出A为INT_MAX B为0 C为0 D为0 (D为~(negative_flag|postive_flag))
    //若负溢出A为0 B为INT_MIN C为0 D为0 (D为~(negative_flag|postive_flag))
    //若正负都没有溢出A B都为0 C为sum D为0xffffffff(D为~(negative_flag|postive_flag))
    //
    //              A                     B                               C
    return sum = (INT_MAX&postive_flag)|(INT_MIN&negative_flag)|(sum&(~(negative_flag|postive_flag)));
}

//减法溢出

截屏2022-02-21 下午9.35.43

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//如果计算x-y不溢出,这个函数就返回1
/**
 * 1.-y根据Abelian Group可以看作是补码的非
 * @param x
 * @param y
 * @return
 */
int tsub_ok(int x,int y)
{
    int sum = x-y;
    int sum_sign_mask = sum>>((sizeof(int)<<3) - 1);
    int x_sign_mask = x>>((sizeof(int)<<3) - 1);
    int negative_y_sign_mask = (-y)>>((sizeof(int)<<3) - 1);
//flag1 flag2 flag3分别对应上图中的flag1 flag2 flag3对应
    int flag1_negative = x<0&&y>0&&sum>0;
    int flag2_positive = x>0&&y<0&&sum<0&&y!=INT_MIN;
    int flag3_positive = x>=0&&y==INT_MIN;
//    printf("%d",flag1_negative);
    //printf("%d",flag2_positive);
//    printf("%d",flag3_positive);
    return !(flag1_negative||flag2_positive||flag3_positive);

}

1.32位乘32位可以用64位表示。

2.unsigned_high_prod的推导:

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-06%20%E4%B8%8B%E5%8D%889.29.32.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//说明该机器可以做64位计算
int signed_high_prod(int x, int y) {
    int64_t prod = 0;
    prod = (int64_t)x * (int64_t)y;
    //printf("%llx\n",prod);
    return prod >> 32;
}
//用于测试结果是否正确
int unsigned_high_prod_test(unsigned x,unsigned y) {

    u_int64_t prod = 0;
    prod = (u_int64_t )x * (u_int64_t )y;
    return prod >> 32;
}

unsigned unsigned_high_prod(unsigned x, unsigned y)
{
    int x_31 = (int)x >>31;
    int y_31 = (int)y >>31;

    unsigned t = (signed_high_prod(x,y)+(y&x_31)+(x&y_31));
    return t;
}

申请一个数组空间,有nmenb个元素,每个元素有size个字节。

注意相乘不溢出的判断。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void *calloc(size_t nmemb, size_t size)
{
    if(nmemb==0||size==0)
    {
        return NULL;
    }
    size_t t = nmemb*size;
    //如果溢出就返回,否则就分配空间 见练习2.
    if (t/nmemb != size)
    {
        return NULL;
    }
    void*p = malloc(t);
    //如果指针为空说明分配空间失败
    if(p==NULL)
    {
        return NULL;
    }

    return memset(p,0,t);
}

如果1连续有A和B两种形式:A形式任何时候都适用,B形式的n=sizeof(int)-1时不适用(P71)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int main() {
    int x =1 ;
    //x*17
    printf("%d\n",(x<<4)+x);

    //x*-7 -7 = 1-8
    x=1;
    printf("%d\n",(x-(x<<3)));

    //x*60 60 = 64 - 4
    x=1;
    printf("%d\n",(x<<6)-(x<<2));

    //x*-112 -112=16 - 128
    x=1;
    printf("%d\n",(x<<4)-(x<<7));


    return 0;
}
1
2
3
4
5
6
7
8
int divide_power2(int x, int k)
{
    //x_sign_mask要么是0xffffffff 要么是0x0
    int x_sign_mask = x>>((sizeof(int)<<3)-1);
    //常规写法 但是这里不允许有?:
    //return x<0? (x+((1<<k)-1))/(1<<k) : x/(1<<k);
    return (x+((1<<k)-1)&x_sign_mask)/(1<<k);
}
1
2
3
4
5
6
7
8
//有3*x溢出
int mul3div4(int x)
{
    int bias = x>>((sizeof(int)<<3)-1);
    x= (x<<1) + x;
    x= (x+((0x3&bias)>>2))>>2;
    return x;
}

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-07%20%E4%B8%8B%E5%8D%889.33.25.png

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

    //假设w为4位 (1111)-1 =(0011)3 +(1100)-4
    //所以低二位一直为正,要进行向上舍入
    //高位也要进行向上舍入
    int high_bias = high_other_bit>>((sizeof(int)<<3)-1);
    //
    low_2_bit = (low_2_bit<<1) + low_2_bit;
    low_2_bit = (low_2_bit + 3)>>2;

    high_other_bit = (high_other_bit+(3&high_bias))>>2;
    high_other_bit = (high_other_bit<<1)+high_other_bit;

    return high_other_bit+low_2_bit;
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void q_1(int k)
{
    //1^(w-k)0^k
    int x = -1<<k;
}

void q_2(int k,int j)
{
    //1^(w-k)0^k
    int x = -1<<j;
    //0^(w-k-j)1^(k+j)
    int y = (1<<(k+j)) - 1;
    int z = x&y;
    //写法二:
    //~(-1 << k) << j
    printf("%.8x",z);
}
截屏2022-02-08 下午12.56.31 截屏2022-02-10 上午10.37.44

目前还存在的问题:在习题最开始的地方说过,不能使用大于小于等比较符号,如何不使用比较符号来解决本题?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//不能使用强制类型转换 看课本P86页最下面
unsigned f2u(float x)
{
    return *((unsigned *)&x);

}

//第一个参数是否小于等于第二个参数
//情况一:两个参数相等且为0 根据IEEE规则,0用Denormalized表示,且有+0(0x0)和-0(0x80000000)两种表示 所以通过左移一位来比较
//情况二:第一个参数为负(此时ux>>31为sx=0xffffffff !sx=0) 第二个参数为0或者正数(此时uy>>31为sy=0x0 !sy=1)
//反之 若第一个参数为正 第二个参数为负(!sy=1,sx=0)
//情况三:两个参数都为负 此时根据IEEE的定义 正数可以用无符号整数的升序进行排列(正数越大 无符号数越大) 负数可以用无符号整数的升序进行排列(负数越小,无符号数越大)
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)|| //情况二
           ((!sx)&&(!sy)&&(ux<=uy))||//情况三:两者都为正数
            (sx&&sy&&(ux>=uy));//情况四:两者都为负数
}
截屏2022-02-10 下午6.35.06 截屏2022-02-10 下午6.35.45

扩展精度形式的缺点:

截屏2022-02-10 下午6.27.35 截屏2022-02-10 下午6.37.17 截屏2022-02-10 下午6.37.46 截屏2022-02-10 下午6.38.10 截屏2022-02-10 下午7.53.47 截屏2022-02-10 下午7.54.35 截屏2022-02-10 下午7.55.13 截屏2022-02-10 下午7.57.59

关于int和float强制类型转换的一点讨论:

截屏2022-02-10 下午8.00.02

解题过程:

截屏2022-02-10 下午8.01.26 截屏2022-02-10 下午8.04.22 截屏2022-02-10 下午8.04.52 截屏2022-02-10 下午8.05.16 截屏2022-02-10 下午8.05.31 截屏2022-02-12 下午1.17.39

存在的问题:不用if等判断如何实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
float_bits float_nega(float_bits f)
{
    //若f为正数,sign为0;若f为负数,sign为0x1。
    unsigned sign = f>>31;
    //exponent field为23位到30位
    unsigned exp = (f>>23)&0xff;
    //fraction field为0到22位 7fffff
    unsigned frac = f&0x7fffff;

    unsigned neg_sign = !sign;

    //如果fraction field不为0且exponent field为0xff
    //如果NaN为1则说明是NaN
    unsigned NaN = (exp==0xff)&&(frac);

    if(NaN)
    {
        return f;
    }

    return (neg_sign<<31)|f;
}
 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
typedef unsigned float_bits;

//unsigned和float有相同的位表示
float_bits f2u(float f)
{
    return *((float_bits *)&f);
}

//unsigned和float有相同的位表示
float u2f(unsigned f)
{
    return *((float *)&f);
}

float_bits float_absval(float_bits f)
{
    unsigned sign = f>>31;
    unsigned exp = (f>>23)&0xFF;
    unsigned frac = f&0x7fffff;

    unsigned NaN = (exp == 0xFF)&&(frac);

    if(NaN)
    {
        return f;
    }

    return exp<<23|frac;
}
截屏2022-02-12 上午10.48.23
 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
typedef unsigned float_bits;
//将unsigned形式的float值 转化为folat类型 方便读取
float u2f(float_bits f)
{
    return *((float *)&f);
}
//将float类型的float值 转为为unsigned类型 方便查看其16进制表示
unsigned f2u(float f)
{
    return *((unsigned*)&f);
}

unsigned NaN(float_bits f)
{
    //提取fraction field到0-23位 其余位为0
    unsigned frac = f&0x7fffff;
    //提取exponent field到0-7位 其余位为0
    unsigned exp = (f>>23)&0xFF;

    //如果frac不为0 且 exp为0xFF说明该浮点数是NaN
    return (frac)&&(exp==0xFF);
}

float_bits float_twice(float_bits f)
{
    //提取sign f为正或0 sign=0 f为负 sign=1
    unsigned sign = f>>31;
    //提取fraction field到0-23位 其余位为0
    unsigned frac = f&0x7fffff;
    //提取exponent field到0-7位 其余位为0
    unsigned exp = (f>>23)&0xFF;

    //如果f是NaN无法进行计算 直接返回即可
    if(NaN(f))
    {
        return f;
    }
    //如果f不是NaN则进行分类讨论
    //情况一:f为一个Denormalized(exp为0) frac左移一位
    if(exp==0)
    {
       frac=frac<<1;
    }
    //情况二:f为+∞或者-∞且不是NaN 此时根据题意不做任何处理
    else if(exp==0xff)
    {

    }
    //情况三:f一定是一个Normalized(exp不为0 exp为全0全1上面已经考虑过)
    else
    {
        //如果exp为1111 1110 则它的两倍会变为∞
        if(exp==0xfd)
        {
            exp = 0xff;
            frac=0x0;
        }
        //否则就是2Valuse还不会越界的f exp+1即可
        exp = exp+1;
    }

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

int main() {
    printf("%.149f\n",u2f(0x1));
    printf("%.149f\n", u2f(float_twice(f2u(u2f(0x1)))));
    return 0;
}

为什么要打印149位?

截屏2022-02-12 上午11.09.01 截屏2022-02-12 上午11.24.15

关于上面特殊情况的解释:

截屏2022-02-12 上午11.26.02 截屏2022-02-12 上午11.28.34
 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
typedef unsigned float_bits;
float u2f(unsigned f)
{
    return *((float *)&f);
}

unsigned f2u(float f)
{
    return *((unsigned *)&f);
}
//如果f为NaN返回1 否则返回0 (exp=0xff frac!=0)
unsigned NaN(float_bits f)
{
    //提取exp
    unsigned exp = f>>23&0xff;
    //提取frac
    unsigned frac = f&0x7fffff;

    return exp==(0xff)&&frac;
}

float_bits float_half(float_bits f)
{
    //提取sign
    unsigned sign = f>>31;
    //提取exp
    unsigned exp = f>>23&0xff;
    //提取frac
    unsigned frac = f&0x7fffff;

    //如果f为NaN,直接返回即可
    if(NaN(f))
    {
        return f;
    }

    //先写出要rounding to even需要加的补偿 用frac+补偿就是rounding to even
    unsigned rounding_add =  (frac&0x3)==0x3;

    //如果f不是NaN,分类讨论
    //如果f为Normalized 则分两种情况考虑
    //情况一:f的exponent field为0000 0001
    if(exp==0x1)
    {
        //exponent field减1 实现除2效果
        exp = exp-1;
        //将expnent field的1移到fraction field最靠近0那位 保证除2效果(具体原因见笔记)
        frac = (frac>>1)+0x400000;
        //加上补偿实现rounding to even
        frac = frac+rounding_add;
    }
    //情况二:f为exp不是0000 0001且不是0x0和0xff的Normalized Values
    else if(exp)
    {
        exp = exp-1;
    }
    //如果f为Denormalized 只有一种情况
    else
    {
        frac = frac>>1;
        frac = frac+rounding_add;
    }
    return sign<<31|exp<<23|frac;

}
截屏2022-02-12 下午1.10.42
 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
typedef unsigned float_bits;

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

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

unsigned is_NaN(float_bits f)
{
    //提取exponent field
    unsigned exp = f>>23&0xff;
    //提取fraction field
    unsigned frac = f&0x7fffff;

    return (exp==0xff)&&frac;
}

int float_f2i(float_bits f)
{
    //提取sign
    unsigned sign = f>>31;
    int sign_int = sign==1 ? -1:1;
    //提取exponent field
    unsigned exp = f>>23&0xff;
    //提取fraction field
    unsigned frac = f&0x7fffff;
    unsigned bias = 127;
    int E = (int)(exp - bias);
    //1.当E小于0时 向零舍入都为0
    if(E<0)
    {
        return 0;
    }
    else if(E>=31)
    {
        return 0x80000000;
    }

    unsigned F = (1<<23)+frac;
    if(E>=23)
    {
        F = F<<(E-23);
    } else
    {
        F=F>>(23-E);
    }
    return sign_int*(int)F;
}
截屏2022-02-12 下午1.09.13 截屏2022-02-12 下午1.09.46 截屏2022-02-12 下午1.10.09
  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
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
typedef unsigned float_bits;

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

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

//判断int类型正数长度
unsigned length(int i)
{
    int length = 0;
    while((unsigned)i>=(unsigned)(1<<length))
    {
        length++;
    }
    return length;
}

//给定一个t长度 求其t位都为1的掩码
unsigned mask(unsigned t)
{
    return (1<<t) -1;
}

float_bits float_i2f(int i)
{
    //提取fraction field
    unsigned frac;

    //提取exponent field
    unsigned exp;

    //提取sign
    unsigned sign;
    //bias为常量
    unsigned bias = 127;

    //如果i为0,进行特使处理
    //如果i为INT_MIN,进行特殊处理
    if(i==0)
    {
        sign = 0;
        frac = 0x0;
        exp = 0;
        return sign<<31|exp<<23|frac;
    }
    //如果i为INT_MIN,进行特殊处理
    if(i==INT_MIN)
    {
        sign = 1;
        frac = 0x0;
        exp = 31+bias;
        return sign<<31|exp<<23|frac;
    }
    //记录i的正负
    unsigned i_sign = !!((unsigned )(i>>31));
    //否则若为 负数先将负数转化为正数再进行处理
    i = i<0 ? -i:i;

    //查看i的最高位1到第0位的长度
    unsigned i_length = length(i);
      //因为此时的float一定为Normalized Value 所以要隐藏最高位
    unsigned f_length = i_length-1;
    //若过此时i的length小于等于24 不需要rouding
    if(i_length<=24)
    {
        //将除最高位的其他位移动到frac的最左边
        frac = (((unsigned)i)&(mask(f_length)))<<(23-f_length);
        //printf("%x\n",mask(10));
        //printf("%x\n",frac);
        exp = f_length+bias;
        //printf("%x\n",exp);
        return i_sign<<31|exp<<23|frac;
    }
    //此时i_length大于24位 float无法精确表示 要进行舍入
    else
    {
        //将前23位(除去最高位和符号位以外的前23位)进行保存
        //
        frac = (((unsigned)i)>>7)&0x7fffff;
       //因为E的大小和f_length长度一样 f_lengt为M=1.fn-1fn-2fn-3fn-4fn-5.. 中f的个数(M=1+f)
        exp = f_length+bias;
      //将frac和exp字段合并 方便后续进行rounding补偿 具体原因见下图
        unsigned frac_exp = exp<<23|frac;
        //将i的后7位保存
        unsigned last_seven_bits = i&0x7f;
        //如果后7位为1000000 则要查看24位中的最后一位
        if(last_seven_bits==0x40)
        {
            //如果最后一位为1,则要整体进1
            if((frac&0x1)==1)
            {
                frac_exp+=1;
            }
            //否则保持不变
        }
        //如果该字段大于0x1000000 则一定要整体进1
        else if(last_seven_bits>0x40)
        {
            frac_exp+=1;
        }
        //如果小于0x40则不进位
        else
        {
                    //不做任何处理
        }
        return i_sign<<31|frac_exp;
    }
}

为什么要将exp和frac进行合并后,再进行rounding?

https://cdn.jsdelivr.net/gh/Nngxu/ImgHosting/PicGo/%E6%88%AA%E5%B1%8F2022-02-12%20%E4%B8%8B%E5%8D%881.08.03.png