【汇编语言与计算机系统结构笔记03】浮点数的计算机表示,IEEE 754,舍入(rounding),C语言中的浮点数

本次笔记内容:
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=−ji​bk​⋅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。

上一篇:Win32子窗口及其创建应用


下一篇:linux – “ls |的结果” wc -l“与实际文件数不匹配