计算机中的浮点数表示

IEEE 754 标准

  在这个标准中,任意一个二进制浮点数 V 可以表示成下面这样的形式:

  1. (-1)^s 表示符号位,当 s = 0 时, V 为正, s = 1 时,V 为负
  2. M 表示有效数字,1 <= M < 2
  3. E 表示指数,如 2^E

  举个例子,对于十进制的 5.0,写成二进制就是 101.0,相当于 1.01 x 2^2

  按照上面的标准,可以得出: s = 0M = 1.01E = 2

  IEEE 754 规定,对于 32 位的浮点数,最高的 1 位是符号位 s,接着的 8 位是指数 E,剩下的 23 位为有效数字 M:

img

  对于 64位 的浮点数,最高的 1位是符号位 S ,接着的 11 位是指数 E,剩下的 52 位为有效数字 M

img

  在 IEEE 754 标准中,对 ME 还有一些特别的规定,用于应对一些极端情况。

  对于M,前面有提到, 1 <= M < 2,也就是说, M 可以写成 1.xxxx 的形式,其中 xxxx 表示小数部分。既然我们知道计算机在保存 M 时第一位默认总是 1,那么我们可以先省略这个数,只保存 xxxx 这样的小数部分,然后在最后的结果前再加上 1,这样,我们就能多出一位来保存小数 xxxx 了。IEEE 754 中就是这样规定的。

  对于E,情况会复杂些:

  首先,E 是一个无符号整数,若 E8 位,它的取值范围就是 0 - 255,若 E11 位,它的取值范围就是 0 - 2047,但是,在科学计数法中,指数位 E 是能取负值的,因此 IEEE 754 中规定,E 的真实值需要再减去一个中间数,相当于是一个偏置(bias),对于 8 位的 E,这个偏置是 127,对于 11 位的 E,这个偏置是 1023

  比如,2^10E10,因此在保存为 32 位浮点数时,需要保存为 10 + 127 = 137,即 10001001(因为 E 在变成 10 之前,减去了一个偏置 127

  E 还能分成三种情况讨论:

  1. 规格化:当 E 的二进制位不全为 0,也不全为 1 时,二进制浮点数 V 为规格化形式。

    这时 E 的计算就按上面的规则,减去 1271023。如 E10000100,则 E = 132E 的真实值 e = 132 - 127 = 5

    M 也是如之前所述,在左侧加上隐含位 1,也就是 1.M ,如 M1010...0101 后面共 20 个 0),则 1.M = 1.101 = 1.625

  2. 非规格化:当 E 的二进制位全部为 0 时,V 为非规格化形式。

    这时,E 的计算就是 1 - 127 (或 1 - 1023),这样做的目的主要是为了规格化数值和非规格化数值之间的平滑过渡。并且,有效数字 M 不再加上第一位的 1,而是按原样输出:0.xxxx。这样,我们就能表示 ±0 (具体看符号位 s)和一些非常逼近 0 的小数了。

  3. 特殊数值: 当E的二进制位全为 1 时为特殊数值。

    此时,若 M 的二进制位全为 0 则表示 ±无穷大(具体看符号位 s),而若 M 的二进制位不全为 0 的话,则表示 NaN(Not a Number),表示这不是一个合法实数或者该数未经初始化。

浮点数例子

  有一段 C 代码是这样的:

int main() {
	int a = 9;
	float* pFloat = (float*)&a;
	printf("%f\n", a);
	printf("%d\n", a);

	*pFloat = 9.0;
	printf("%f\n", *pFloat);
	printf("%d\n", a);

	return 0;
}

  程序的打印结果:

image-20201005230741731

  在上面的代码中,我们先申明了一个整型 a = 9,然后申明了一个浮点指针 pFloat 指向 a 的地址。

  1) 使用浮点数格式打印 a 时,发现为 0.000000

  2) 然后我们根据 a 的地址 pFloat 修改它的值为 9.0,此时再以整型来打印 a,输出的却是 1091567616

  首先来看 1) ,为什么整型的 9 以浮点格式输出时打印出来的是 0.000000

  将 32 位的整型 9 转化为二进制形式,得到: 0 0000 0000 000 0000 0000 0000 0000 1001

  根据前面的知识,可以知道 s = 0E = 0M = 0...1001,指数 E 的二进制位全为 0,因此是第二种非规格化的情况,因此浮点数 V 就写成:

V = (-1)^0 x 0.00000000000000000001001 x 2^(-126) = 1.001 x 2^(-146)

  显然,V 是一个非常小的接近 0 的数,因此用十进制小数表示就是 0.000000

  再来看看 2) ,为什么内存中的 9 变成 9.0 后,以整型格式打印,结果会是 1091567616

  将浮点数 9.0 转换为二进制小数形式:1001.01.001 x 2^3 ,那么,我们可以知道 s = 0E = 3 + 127 = 130,二进制表示的话就是 10000010M = 100 1000 0000 0000 0000 0000 ,因此,9.0 的二进制形式表示就是:s + E + M ,也就是:0 1000 0010 100 1000 0000 0000 0000 0000 ,这个 32 位的二进制,用十进制表示就是:1,091,567,616

其他

  很多小数不能准确表示成二进制形式。比如 0.4,你就无法写出它的精确二进制形式(即 0.5的各次幂的累加),我们只能不断接近 0.4,而不能达到它。

  因为 0.4 的二进制表示是:0.01100110011.... 无限循环,因此我们只能逼近而不能精确表示。


REF:


 目录