1
2
|
log:
2022-2-21 对2.32和2.74进行了修正
|
代码在2.74处
正溢出一定为负,负溢出可能为0或正
![截屏2022-02-07 下午6.53.48 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 感觉写的很对很清晰):
所以有2.32的正式解题思路:
在解题前,先推导一个小知识点:
(无论是unsigned 的x、y 还是two’s complement的x、y)w位的x和w位的y相乘结果最多要2w位表示。
证明如下:
![截屏2022-02-06 下午11.03.26 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]()
![截屏2022-02-06 下午11.02.40 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解题思路:
![截屏2022-02-06 下午10.55.16 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]()
![截屏2022-02-06 下午10.55.33 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:
方法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:
方法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)
|
使用算术右移来代替逻辑右移
- ~((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))));
}
|
使用逻辑右移来实现算术右移
- ~((k!=0)-1))的作用(相当于if的作用)
当k为0时,上式为0x0保证了和前面相与为0
当k为1时,上式为0xffffffff,保证了和前面相与结果不会产生影响。
-
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.点圆为任意位级运算
![截屏2022-02-01 下午1.45.31 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)));
}
|
//减法溢出

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的推导:
![截屏2022-02-06 下午9.29.32 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;
}
|
![截屏2022-02-07 下午9.33.25 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);
}
|
目前还存在的问题:在习题最开始的地方说过,不能使用大于小于等比较符号,如何不使用比较符号来解决本题?
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));//情况四:两者都为负数
}
|
扩展精度形式的缺点:
关于int和float强制类型转换的一点讨论:
解题过程:
存在的问题:不用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;
}
|
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位?
关于上面特殊情况的解释:
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;
}
|
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;
}
|
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?
![截屏2022-02-12 下午1.08.03 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]()