浮点数的加减乘除。
# Lecture05 - 浮点数运算
# 回顾
记忆 1+8+23 的(符号位移码原码)
记忆各种情况对应的表示
# 加法和减法
检查 0:如果有 0 存在可以不用计算
对齐有效位:阶码向大值对齐,因为右移较小的数而丢失的数字所造成的的影响较小。右移较小的数有效值的幅值部分 1 位,并将阶值加 1。如果两个数的阶值差别非常大,则较小的数丢失
加或减有效值:原码加减法
规格化结果:把结果调整为左移有效值直到最高有效数字为非 0。
(右规最多是两位,最多是 1.1111...+1.xxxx,对应有效值的上溢)
# 溢出
- 阶值上溢
最大允许阶值 127(11111110)
右移可能会导致阶值的上溢
阶值下溢
最小阶值为 - 126(00000001)
左移可能会引起阶值的下溢
有效值的上下溢只存在于右规
# 异常
关于数太小阶下溢的问题,在 cpp 中,如果是单精度浮点数,会用非规格化数表示,如果是双精度浮点数,会直接用 0 表示。
# 原码的加法
求补的时候加了 2 的 n 次方
有进位说明 a>b
无进位说明 a<b
01111110 ——127 23 表示有 23 个零
01111101 ——126 21 表示有 21 个零
差了一位,左移一位
1111110 127
1111101 126
然后给 0.4375 取个反
# 乘法和除法
注意乘法和除法的阶值计算 ——
- 乘法加 bias
- 除法减 bias
- bias 的值为 127【通常,移码的偏移量为 2k-1 -1,移码的偏移量主要是看想表示多少个负数和多少个正数,阶码的范围是 - 126~127,-127 和 128 分别表示特殊的数】
# 乘法
对于乘法来说,只有右规。【1.x * 1.x 只可能超出】
最高两位为 01,不用处理。
# 除法
x 和 y 可能都是 0,可能会报错或者是正负无穷
对于除法来说,只有左规。【1.x/ 1.x 】
# 精度保护
y 需要左移一位对齐,所以最后一位 1 在不使用附加位的情况下会丢失。
一般而言,多余位的值超过了最低可表示位值的一半,则进位。
重点关注 “10” 强制结果为偶数的分类讨论。如果结果的最低可表示位是 1,结果向上入;当最低可表示位是 0,结果向下入。
朝 0 摄入,被截断值的幅值总是小于或等于更精确原值的幅值,在计算中产生一致的向下偏差。
# 精度考虑
x == (int)(float) x
int 型有 32 位,但是 float 精度只能保存 24 位,会有精度的损失。
x * x >= 0 否
(D + F) - D == F 什么时候不成立?
右边计算出来为 double 型,左边是 float
float f = 1.0f; | |
for(int i = -100; i < 100; i++){ | |
double d = pow(2,i); | |
if((d + f - d) == f){ | |
cout << i << endl; | |
} | |
} |
对于此例情况,当 i 大于 53 或等于 - 53 时,会输出。
因为此时 f 相对于 d 较小,相当于 0。
补充:双精度 64(1+11+52)小数部分有 52
位。别的情况,i 会有不同的值对应。
i 大于 53 的情况
舍入位虽然是 10,但前置位已经是 0(偶数),因此不进位,所以有精度的丢失。
i 等于 - 53 的情况
舍入位是 10,前置位是 1,因此要进位,从而造成了误差。
只要有精度的丢失,就是 “否”