本次笔记内容:
04.浮点数的计算机表示
文章目录
IEEE的浮点数标准
IEEE的754标准
在1985年建立,如下表。
2^i | 2^{i-1} | … | 4 | 2 | 1 | |||||
---|---|---|---|---|---|---|---|---|---|---|
b_i | b_{i-1} | … | b_2 | b_1 | b_0 | b_{-1} | b_{-2} | b_{-3} | … | b_{-j} |
1/2 | 1/4 | 1/8 | … | 2^{-j} |
可以看出,其二进制表示方式为 ∑ k = − j i b k ⋅ 2 k \sum^i_{k=-j} b_k \cdot 2^k ∑k=−jibk⋅2k。
浮点数示例
下例中,“-”代表又。
- 5-3/4:101.11_2
- 2-7/8:10.111_2
- 63/64:0.111111_2
可以看出,存在局限性:
只能精确地表示X/2^k这类形式的数据,而对于下列数据,不能精确表示:
- 1/3:0.0101010101[01]…_2
- 1/5:0.001100110011[0011]…_2
- 1/10:0.0001100110011[0011]…_2
计算机中浮点数二进制表示
数字形式: ( − 1 ) s M 2 E (-1)^s M 2^E (−1)sM2E
- 符号:s
- 尾数:M,是一个位于区间[1.0, 2.0)内的小数
- 阶码:E
编码形式:
exp域:E(注意,E要进行变换,再存储在exp中);
frac域:M。
- 单精度浮点数:exp域宽度为8bits,frac域宽度为23bits,总共32bits;
- 双精度浮点数:exp域宽度为11bits,frac域宽度为52bits,总共64bits;
- 扩展精度浮点数:exp域宽度为15bits,frac域宽度为63bits,总共80bits。(1 bit wasted)
浮点数的类型
- 规格化浮点数
- 非规格化浮点数
- 一些特殊值
规格化浮点数(Normalized)
- 满足条件:exp不全为0且不全为1。
- 真实的阶码值需要减去一个偏置(biased)量:
-
- E = Exp - Bias
-
- Exp:exp域所表示的无符号数值
-
- Bias的取值:
-
-
- 单精度数:127(Exp:1…254,E:-126…127)
-
-
-
- 双精度数:1023(Exp:1…2046,E:-1022…1023)
-
-
-
- Bias = 2^{e-1} - 1,e = exp的域的位数
-
- frac的第一位隐含1:M = 1.xxx…x_2
-
- 因此第一位的“1”可以省去,xxx…x:bits of frac
-
- Minimum when 000…0 (M = 1.0)
-
- Maximum when 111…1 (M = 2.0 - \epsilon)
规格化浮点数示例
Float F = 15213.0;
// 二进制
15213_10 = 11101101101101_2
// 二进制向右移13位,再乘2^13
1.1101101101101_2 * 2^13
// 则其尾数为
M = 1.1101101101101_2
// 取小数部分,在计算机中存储为
frac = 11011011011010000000000
// 其阶码为
E = 13
Bias = 127
// 阶码在计算机中存储为,加上偏置量
Exp = 140 = 10001100
最终,15213.0在计算机中的存储为(第二行):
Hex | 4 | 6 | 6 | D | B | 4 | 0 | 0 |
---|---|---|---|---|---|---|---|---|
Binary | 0100 | 0110 | 0110 | 1101 | 1011 | 0100 | 0000 | 0000 |
140 | _100 | 0110 | 0____ | |||||
15213 | (1)110 | 1101 | 1011 | 01__ |
上表中,M取值一定位1.x,因此15213行的首个1省略。
非规格化浮点数(Denormalized)
- 满足条件:exp全为0。
- 其他域的取值
-
- E = -Bias + 1;
-
- M = 0.xxx…x_2
-
- xxx…x:bits of frac
为什么Bias取2^{e-1} - 1(e = exp的域的位数)?或者说,为什么在规格化浮点数情况下不允许exp取全0?
答:在不考虑符号位的情况下,考虑规格化浮点数的最小取值:首先E应该取1(exp为1减去偏移量即1-Bias),frac取1.0…。如果有数字,比这个数还小一点点,则只能将frac小数点再左移。此时,则需要exp全0这种表达,表示此时frac是0.开头,而非1.开头。0.开头即非规格化浮点数。
非规格化浮点数示例
- exp = 000…0,frac = 000…0
-
- 表示0,注意有+0与-0(由s位决定)。
- exp = 000…0,frac不等于0
-
- 表示“非常接近”于0的浮点数;
-
- 会逐步丧失经度,称为“Gradual underflow”。
一些特殊值
- 满足条件:exp全为1。
一些特殊值具体示例
- exp = 111…1,frac = 000…0
-
- 表示无穷,可用于表示数值的溢出
-
- 有正无穷与负无穷之分:1.0 / 0.0 = +∞;-1.0 / 0.0 = -∞
- exp = 111…1,frac 不等于全0
-
- Not-a-Number(NaN)
-
- E. g. sqrt(-1), ∞ - ∞, ∞ * 0
各种浮点数类型在数轴上的相对位置
实例:一种“小”浮点数
- 8位浮点数表示:exp域宽度为4 bits,frac域宽度为3 bits。则,其偏置量的值为2^(4-1) - 1 = 7.
- 其他规则符合IEEE 754规范。
取值范围如下表。
s | exp | frac | E | value |
---|---|---|---|---|
0 | 0000 | 000 | -6 | 0 |
0 | 0000 | 001 | -6 | 1/8 * 1/64 = 1/512 |
0 | 0000 | 010 | -6 | 2/8 * 1/64 = 2/512 |
… | ||||
0 | 0000 | 110 | -6 | 6/8 * 1/64 = 6/512 |
0 | 0000 | 111 | -6 | 7/8 * 1/64 = 7/512 |
0 | 0001 | 000 | -6 | 8/8 * 1/64 = 8/512 |
0 | 0001 | 001 | -6 | 9/8 * 1/64 = 9/512 |
… | ||||
0 | 0110 | 110 | -1 | 14/8 * 1/2 = 14/16 |
0 | 0110 | 111 | -1 | 15/8 * 1/2 = 15/16 |
0 | 0111 | 000 | 0 | 8/8 * 1 = 1 |
0 | 0111 | 001 | 0 | 9/8 * 1 = 9/8 |
0 | 0111 | 010 | 0 | 10/8 * 1 = 10/8 |
… | ||||
0 | 1110 | 110 | 7 | 14/8 * 128 = 224 |
0 | 1110 | 111 | 7 | 15/8 * 128 = 240 |
0 | 1111 | 000 | n/a | inf |
可以看出,在不考虑符号位s时,较好通过浮点数二进制表示方式比较大小。
浮点数的一些编码特性
- (几乎)可以直接使用无符号整数的比较方式;
- 反例:
-
- 必须先比较符号位
-
- 考虑+0、-0的特例
-
- 还有NaN的问题
- (不考虑符号位的话)NaN比其他值都大
- 实际的比较结果如何?(自行实现)
其他情况都可以直接使用无符号整数的比较方式:
- 规格化 vs. 非规格化
- 规格化 vs. 无穷
Rounding(舍入)
给定一个实数,如何给出其浮点数表示?
基本流程:
- 首先计算出精确值;
- 然后将其转换为所需的精度;
- 可能会溢出(如果指数绝对值很大);
- 可能需要完成舍入(rounding)操作。
各种舍入模式
1.40 | 1.60 | 1.50 | 2.50 | -1.50 | |
---|---|---|---|---|---|
Zero | 1 | 1 | 1 | 2 | -1 |
Round down | 1 | 1 | 1 | 2 | -2 |
Round up | 2 | 2 | 2 | 3 | -1 |
Nearest Even(default) | 1 | 2 | 2 | 2 | -2 |
Nearest Even为向最近的偶数舍入(并非四舍五入)。是计算机内默认的舍入方式。
向偶数舍入(Round-To-Even)
这是计算机内默认的舍入方式,也称为“(将0.5)向最接近值的舍入”。
- 其它方式会产生系统误差(statistically biased)
关键的设计决策的是确定两个可能结果的中间数值的舍入,确保舍入后的最低有效数字是偶数。
E.g., round to nearest hundredth
1.2349999 | 1.23 | (Less than half way) |
---|---|---|
1.2350001 | 1.24 | (Greater than half way) |
1.2350000 | 1.24 | (Half way - round up) |
1.2450000 | 1.24 | (Half way - round down) |
对于二进制而言
实例如下表,舍入到小数点后2位:
Value | Binary | Rounded | Action | Rounded Value |
---|---|---|---|---|
2 3/32 | 10.00 011 | 10.00 | (<1/2 - down) | 2 |
2 3/16 | 10.00 110 | 10.01 | (>1/2 - up) | 2 1/4 |
2 7/8 | 10.11 100 | 11.00 | (1/2 - up) | 3 |
2 5/8 | 10.10 100 | 10.10 | (1/2 - down) | 2 1/2 |
可以看出,“Even”意味着如下规则:
- 只有当被舍位为100…(如表中后两行)时,才考虑“Even”舍入规则;
- 规则为,要让舍入后的二进制数,最低位为0。
具体步骤
- 将数值规格化(前导1)
- 舍入(round to even)以便符合尾数位数需求
- 后调整
实例
将8位无符号数转换为8位浮点数(exp域宽度为4 bits,frac域宽度为3 bits)
首先,规格化:
Value | Binary | Fraction | Exponent |
---|---|---|---|
128 | 10000000 | 1.0000000 | 7 |
15 | 00001101 | 1.1010000 | 3 |
17 | 00010001 | 1.0001000 | 4 |
19 | 00010011 | 1.0011000 | 4 |
138 | 10001010 | 1.0001010 | 7 |
63 | 00111111 | 1.1111100 | 5 |
接下来,舍入:
Value | Fraction | Incr? | Rounded |
---|---|---|---|
128 | 1.000 0000 | N | 1.000 |
15 | 1.101 0000 | N | 1.101 |
17 | 1.000 1000 | N | 1.000 |
19 | 1.001 1000 | Y | 1.010 |
138 | 1.000 1010 | Y | 1.001 |
63 | 1.111 1100 | Y | 10.000 |
其中,17、19由于是舍去1+0*,因此要求Rounded之后以0结尾。
最后,调整:
Value | Rounded | E | Adjusted | Result |
---|---|---|---|---|
128 | 1.000 | 7 | 128 | |
15 | 1.101 | 3 | 15 | |
17 | 1.000 | 4 | 16 | |
19 | 1.010 | 4 | 20 | |
138 | 1.001 | 7 | 134 | |
63 | 10.000 | 5 | to 1.000, E = 6 | 64 |
C语言中的浮点数
- float 单精度浮点数;
- double 双精度浮点数。
当int(32位宽),float,与double等类型间进行转换时,基本的原则如下:
- double或float转换为int:
-
- 尾数部分截断;
-
- 如果溢出或者浮点数是NaN,则转换结果没有定义,通常置为Tmin or Tmax。
- int转换为double:
-
- 能够精确转换。
- int转换为float:
-
- 不会溢出,但是可能被舍入。
Floating Point Puzzles
以下判断是否成立,如果不成立请给出反例。
int x = foo();
float f = bar();
double d = foobar();
假设d与f都不是NaN。
x == (int)(float) x
不成立,float有效位数不够。
x == (int)(double) x
成立。
f == (float)(double) f
成立。
d == (float) d
不成立。
f == -(-f)
成立。
2/3 == 2/3.0
不成立。
因为2/3是整数运算,等于0,而右侧是浮点数运算。
d < 0.0 infer ((d*2) < 0.0)
成立。因为浮点数是逐渐丧失精度,可以变成负无穷。
d > f infer -f > -d
成立。
d * d >= 0.0
成立。不存在有符号整数突变为负数的情况。
(d+f) - d == f
不成立。由于f的精度低,因此,d+f时f容易被忽略。
例题
给定一个浮点格式,有k位指数和n位小数,对于下列数,写出阶码E、尾数M、小数f和值V的公式。另外,请描述其位表示。
问0:E、M与f、V
B i a s = 2 k − 1 − 1 Bias = 2^{k-1} - 1 Bias=2k−1−1
E = e x p − B i a s E = exp - Bias E=exp−Bias
V = ( − 1 ) s M 2 E V = (-1)^s M 2^E V=(−1)sM2E
E最大值为 2 k − 1 − ( 2 k − 1 − 1 ) = 2 k − 1 2^k - 1 - (2^{k-1} - 1) = 2^{k-1} 2k−1−(2k−1−1)=2k−1。
问1:数5.0
5.0 // 转换为二进制 ==>
101 // 进位,直到取最左1 ==>
M = 1.01 // 此时,E = 2
frac= 01 0* // 共n位
exp = E + Bias
= 2 + (2^(k-1) - 1)
则,位的描述为:
s | exp | frac |
---|---|---|
0 | bin(2 + 2^(k-1) - 1) | 01 0000…(共n位, 开头为01, 0补其他位) |
问2:能够被准确描述的最大奇数
参考上文实例:一种“小”浮点数中的表格,思路如下:
frac有n位,则M可视为 1 + 1 2 n × C 1+\frac{1}{2^n} \times C 1+2n1×C;
其中,C是整数,由frac决定,即 C = o c t ( f r a c ) C=oct(frac) C=oct(frac);
并且C满足 0 ≤ C ≤ 2 n − 1 0 \le C \le 2^n - 1 0≤C≤2n−1。
默认V为正数(即s=0),则可将V表示为:
V = ( 1 + 1 2 n × C ) × 2 E = 2 E + 2 E − n × C V=(1+\frac{1}{2^n} \times C) \times 2^E = 2^E + 2^{E-n} \times C V=(1+2n1×C)×2E=2E+2E−n×C
则现在的任务有两个:
- 不能有小数(C为小数,则E不可以大于n);
- 是奇数( 2 E 2^E 2E是奇数则过于浪费,因此使 2 E − n × C 2^{E-n} \times C 2E−n×C为奇数)。
下面分类讨论:
情况一:E可以取到n时,
即 2 k − 1 ≥ n 2^{k-1} \ge n 2k−1≥n时,
E取n,C取其能取的最大奇数,即1* 01(保证最右两位是01, 其他位为1)。
情况二:E*取不到n时,
即 2 k − 1 ≤ n 2^{k-1} \le n 2k−1≤n时(不太可能),
E取最大即 2 k − 1 2^{k-1} 2k−1,而C取 2 n − E 2^{n-E} 2n−E(为了约掉后一项小数)。
问3:最小的正规格化数
exp为0* 1,frac为0*。
E取最小,即 e x p m i n − B i a s = 2 0 − ( 2 k − 1 − 1 ) exp_{min} - Bias = 2^0 - (2^{k-1} - 1) expmin−Bias=20−(2k−1−1)。
十进制即为 1 × 2 ( 2 0 − ( 2 k − 1 − 1 ) ) = 2 2 − 2 k − 1 1 \times 2^{(2^0 - (2^{k-1} - 1))} = 2^{2 - 2^{k-1}} 1×2(20−(2k−1−1))=22−2k−1。