---恢复内容开始---
前面的空缺是我忘了保存
2.77
使用位移运算和+- 实现乘法运算
A k=17 C=x<<4+x
B k=-7 C=x-(x<<3)
C k=60 C=x<<6-x<<2
D k=-112 就不做了
2.78
写出具有如下原型的函数
int divide_power2(int x,int k){
int w=sizeof(int)*8;
int mask=x>>w-1;
int bios=(1<<k)-1;
// printf("%d",bios);
return(x+bios&mask)>>k;
}
使用负数掩码添加bios的技巧
2.79
计算函数
int mul3div4(int x){
x=x<<1+x;
return divide_power2(x,2);
}
//这个版本只需要先乘然后除就可以了
2.80
新的版本不要求溢出
int threefourths(int x){
int b1=divide_power2(x,1);
int b2=divide_power2(x,2);
return b1+b2;
}
//这个版本可以改进的,所有的掩码应该是一样的,我就不写了
2.81
A. 1[w-n]0[n]: ~((1<<n) - 1)
B. 0[w-n-m]1[n]0[m]: ((1<<n) - 1) << m
2.82
A x<y == -x>-y
对于大部分正负合理的数是对的 ,但是极端数当 x等于 INT_MIN时不对
B (((x+y)<<4)+y-x)==17y+15x 只是加减构成了阿贝尔群 , 这样的等是没有问题
C ~x+~y+1= ~(x+y) 这个因该是对的 有这个式子 -x=~x+1 恒成立
D 对的 (ux-uy)==-(unsigned)(y-x)
E ((x>>2)<<2)<=x 因该是对的 最后的两位被归零了 其余的位不变
2.83
A. 令x为无穷序列表示的值,可以得到x*2^k = Y + x。 所以 x = Y/(2^k - 1)。 B. (a)1/7, (b)9/15 = 3/5, (c)7/63 = 1/9
表示无穷的方法
2.84
通过符号位来判断float的大小这种方法是有非常好的性质,因为浮点数的自然顺序就是如此
还是漏掉了一种方法。
有四种不同的情况
一正一负
都是零
同正
同负
((ux<<1)==0 && (uy<<1)==0) || (!sx && sy) || (!sx && !sy && ux >= uy) || (sx && sy && ux <= uy);
2.85
有k位指数 n位小数
写出阶码E,尾数M,小数f,和值V得公式
数7.0
E =10011 (共k位)
f =11000共n位
尾数
###够被准确描述的最大奇整数###
小数f 1111(全一 共N位 )
阶码 10011 (值是n) 2^(n-1) 这么大
值因该是
11111111 有n+1个 1
这个值是 2^n -1
最小的规格化数的倒数
最小的规格化数
阶码
00000001 -2^(k-1) +2// 1-bios bios=2^(k-1)-1
小数
0000000 全零代表 1
值 2^{-2^(k-1)+2}
取导之后是
2^{ 2^(k-1) -2}
阶码是 11101 只有第1位是0 // 值应该是 2^k-1 -2 + bios
2^(k-1)
尾数是全零表示1
阶数的符号表示不是补码表示,进行计算的时候要带上bios
2^k-1 -1 这个是bios 换句话讲阶数等于0是这样的 01111111 大于零的阶数首位必定是1
2.86
精度扩展的问题
80位字节下浮点数的一些特值
1 个符号位
15个阶码位
1个整数位 规格化数位1 非规格化数值为零
63个小数位
最小的 正非规格化数
值
2^( -2^(14) +###2### ) * 2^(-63)
//非规格化数阶数为1- bios 为了平滑的过渡
3.64510^(-4951)
最小的正规格化数
2^(-2^14 +2 )
最大的规格化数
(2-2^(-63))2^{2 ^(14))}
2.87
在 一个符号位 五个阶码位 10个尾数位 时填表
描述 | Hex | M | E | V | D |
---|---|---|---|---|---|
-0 | 8000 | 0 | 0 | -0 | * |
最小的大于2的值 | 4001 | 0000000001 | 10000 | 2 +2^-9 | * |
512 | 6000 | 0000000000 | 11000 | 512 | 512.0 ---恢复内容结束--- 前面的空缺是我忘了保存 |
2.77
使用位移运算和+- 实现乘法运算
A k=17 C=x<<4+x
B k=-7 C=x-(x<<3)
C k=60 C=x<<6-x<<2
D k=-112 就不做了
2.78
写出具有如下原型的函数
int divide_power2(int x,int k){
int w=sizeof(int)*8;
int mask=x>>w-1;
int bios=(1<<k)-1;
// printf("%d",bios);
return(x+bios&mask)>>k;
}
使用负数掩码添加bios的技巧
2.79
计算函数
int mul3div4(int x){
x=x<<1+x;
return divide_power2(x,2);
}
//这个版本只需要先乘然后除就可以了
2.80
新的版本不要求溢出
int threefourths(int x){
int b1=divide_power2(x,1);
int b2=divide_power2(x,2);
return b1+b2;
}
//这个版本可以改进的,所有的掩码应该是一样的,我就不写了
2.81
A. 1[w-n]0[n]: ~((1<<n) - 1)
B. 0[w-n-m]1[n]0[m]: ((1<<n) - 1) << m
2.82
A x<y == -x>-y
对于大部分正负合理的数是对的 ,但是极端数当 x等于 INT_MIN时不对
B (((x+y)<<4)+y-x)==17y+15x 只是加减构成了阿贝尔群 , 这样的等是没有问题
C ~x+~y+1= ~(x+y) 这个因该是对的 有这个式子 -x=~x+1 恒成立
D 对的 (ux-uy)==-(unsigned)(y-x)
E ((x>>2)<<2)<=x 因该是对的 最后的两位被归零了 其余的位不变
2.83
A. 令x为无穷序列表示的值,可以得到x*2^k = Y + x。 所以 x = Y/(2^k - 1)。 B. (a)1/7, (b)9/15 = 3/5, (c)7/63 = 1/9
表示无穷的方法
2.84
通过符号位来判断float的大小这种方法是有非常好的性质,因为浮点数的自然顺序就是如此
还是漏掉了一种方法。
有四种不同的情况
一正一负
都是零
同正
同负
((ux<<1)==0 && (uy<<1)==0) || (!sx && sy) || (!sx && !sy && ux >= uy) || (sx && sy && ux <= uy);
2.85
有k位指数 n位小数
写出阶码E,尾数M,小数f,和值V得公式
数7.0
E =10011 (共k位)
f =11000共n位
尾数
###够被准确描述的最大奇整数###
小数f 1111(全一 共N位 )
阶码 10011 (值是n) 2^(n-1) 这么大
值因该是
11111111 有n+1个 1
这个值是 2^n -1
最小的规格化数的倒数
最小的规格化数
阶码
00000001 -2^(k-1) +2// 1-bios bios=2^(k-1)-1
小数
0000000 全零代表 1
值 2^{-2^(k-1)+2}
取导之后是
2^{ 2^(k-1) -2}
阶码是 11101 只有第1位是0 // 值应该是 2^k-1 -2 + bios
2^(k-1)
尾数是全零表示1
阶数的符号表示不是补码表示,进行计算的时候要带上bios
2^k-1 -1 这个是bios 换句话讲阶数等于0是这样的 01111111 大于零的阶数首位必定是1
2.86
精度扩展的问题
80位字节下浮点数的一些特值
1 个符号位
15个阶码位
1个整数位 规格化数位1 非规格化数值为零
63个小数位
最小的 正非规格化数
值
2^( -2^(14) +###2### ) * 2^(-63)
//非规格化数阶数为1- bios 为了平滑的过渡
3.64510^(-4951)
最小的正规格化数
2^(-2^14 +2 )
最大的规格化数
(2-2^(-63))2^{2 ^(14))}
2.87
在 一个符号位 五个阶码位 10个尾数位 时填表
描述 | Hex | M | E | V | D |
---|---|---|---|---|---|
-0 | 8000 | 0 | 0 | -0 | * |
最小的大于2的值 | 4001 | 0000000001 | 10000 | 2 +2^-9 | * |
512 | 6000 | 0000000000 | 11000 | 512 | 512.0 |
最大非规格化数 | 7fff | 1111111111 | 11111 | (2 - 2^-10 )*2^16 | * |
-∞ | fc00 | * | * | * | * |
M 应该是小数值
E 应该是阶码的值,我就不再改了
2.88
浮点数的转换
位 | 值 | 位 | 值 |
---|---|---|---|
1 01110 001 | -9/16 | 1 0110 0010 | -9/16 |
0 10110 101 | 416 | 0 1110 1010 | 416 |
1 00111 110 | -9/1024 | 0 0000 0111 | -7/1024 |
0 00000 101 | 5*2^12 | 0 0000 0000 | 0 |
1 11011 000 | -2^12 | 1 1111 0000 | -∞ |
0 11000 100 | 3*2^8 | 0 111 1111 | (2-2^4)*2^8 |
2.89
另外在32位的机子上double 和 flaot 是使用了80位的扩展精度(我的机子上只有longdouble 是80 位 的)
喜闻乐见的找错误环节
int x=random()
double dx=(double)x
A (float)x==(float)dx 错的,单精度浮点数误差 最多表示一个数最高位1和最低的1相差不超过24位的数 ,超过这个范围没有办法表示了
B dx-dy ==(double)(x-y) 显然是错的
C (dx+dy)+dz==dx+(dy+dz) 经典的错误 浮点数的结合律会被舍入规则坑掉的
D dx/dx=dz/dz 其中一个是0的话有可能出来Nan的
2.90
偷个懒就是判断一些可以有效表示的范围
2.91-2.97
实现在 flaot_bits 上的函数 我就先不写了