小数的概念新解:从基础到浮点数存储原理
小数的概念新解:从基础到浮点数存储原理
小数是数学中最基础的概念之一,但其背后的原理却远比我们想象的要复杂。本文将从基础的小数概念出发,深入探讨小数的分类以及浮点数在计算机中的存储方式。
一、小数的概念
小数的概念看似简单,实则不然。让我们尝试回答以下几个问题:
- 所有的分数都是小数吗?
- 所有的小数都是分数吗?
- 8.0是不是小数?
- 8是不是小数?
- 整数是不是小数?
- 有理数是不是小数?
- 无理数是不是小数?
- 实数是不是小数?
小学课本中对小数的定义较为简单:
而百度百科中的定义则更为准确:
小数是实数的一种特殊的表现形式。所有分数都可以表示成小数,小数中的圆点叫做小数点,它是一个小数的整数部分和小数部分的分界号。其中整数部分是零的小数叫做纯小数,整数部分不是零的小数叫做带小数。
这里有两个关键词:实数、表现形式。"小数是实数的一种特殊的表现形式",这句话道出了小数的本质:
- 小数是数的一种表现形式
- 只要是实数,都能用小数的形式表现出来
因此,说到底,小数只不过是数的一种写法而已。只要一个数能写成这样的形式:整数部分.小数部分,那么它就是小数。所以说,小数完全是形式化的东西。
用一张图可以表示出小数与实数范围内其他数的关系:
因此,前面提的那一大堆问题,只有"8.0是不是小数?"这一条是肯定的。
据此,我们小学时都学到的一个说法可能是不严谨的:π是无限不循环小数。严格来讲,我们可以说"π可以写成无限不循环小数",但不能说"π是无限不循环小数"。
二、小数的分类
1. 有限小数、无限循环小数、无限不循环小数
按小数点后的数字的性质可分为:
- 有限小数:小数点后只有有限个数字的小数,如0.5、0.25、1.75等。有限小数都可以表示为两个整数的比(分母不为0)。
- 无限小数:小数点后有无限多个数字。分为两类:
- 无限循环小数:小数点后的数字序列中,有一部分数字会无限重复。比如0.333…(或写作0.3)、0.142857142857…(或写作0.142857)。无限循环小数也可以表示为两个整数的比。
- 无限不循环小数:小数点后的数字序列不会重复,也不会终止。比如π、e(自然对数的底数)、2等。无限不循环小数不能表示为两个整数的比。
2. 纯小数、带小数
按整数部分是否为0可分为:
- 纯小数:整数部分为0的小数。
- 带小数:整数部分不为0的小数。
3. 定点数、浮点数
按小数在计算机中的表示形式可分为:
- 定点数:指小数点在数中的位置是固定不变的。因为位置固定,所以实际上不需存储小数点。理论上讲,可以约定把小数点放在任何位置,但通常将其放在最后(纯整数:除符号位,所有数都表示整数)或最前(纯小数:除符号位,所有位都表示纯小数)。
- 浮点数:小数点的位置是不固定的,这使得浮点数可以表示非常大或非常小的数。浮点数通常使用科学计数法来表示,即M × R E M \times R^EM×RE,其中 M 是尾数(或有效数字),E 是指数(或阶码),R是进制。尾数和阶码均为带符号数。尾数的符号表示数的正负,阶码的符号则表明小数点的实际位置。浮点数的精度由尾数决定,数的表示范围由阶码决定。如二进制小数10011.101,按浮点数形式可表示为1.0011101×24。其中尾数为+1.0011101,阶码为+4。
三、浮点数的存储
浮点数的存储涉及到补码知识:
- 正数补码都是其自身。
- 负数的补码是对应正数取反,再+1。
此外,还涉及到另一个概念:移码。移码,就是对补码的符号位取反。假设用4位二进制数表示,-1的补码为1111,移码为0111;1的补码为0001,移码为1001。0111在十进制下为7,1001在十进制下为9,相当于给原数加上了8(23)。速算规律:n位二进制数的移码相当于加上2n-1。
在C/C++中,存放浮点数的变量类型为float和double,分别占用32位、64位二进制数,其空间的分配规则如下:
以float类型数据为例,在32位二进制空间中,从左往右:
- 符号位:最高位为符号位,用来表示正负(0为正数、1为负数)。
- 指数位:接下来8位为指数位,用来存储浮点数阶码。只是实际存储的数据并非阶码的二进制值,而是将阶码先转为移码,再减1。也就是说,存入的数据为:阶码+27-1=阶码+127)。
- 尾数位:再之后23位用来表示尾数部分(纯小数)。因为浮点数的尾数都为1.××××,所以可将1和小数点都舍去,只存储小数点后边的部分。
例如,十进制数19.625,若以float类型存储,计算过程如下:
- 浮点数:19.625转换为二进制数为10011.101,浮点数表示为1.0011101×24。
- 符号位:正数,所以符号位为0。
- 阶码位:阶码为+4,8位二进制对应存储数据为:阶码+127=4+127=131=10000011B。
- 尾数位:尾数为1.0011101,舍去前面的1和小数点,只存储后面的0011101。
三个部分合在一起,得到19.625在内存中的存储形式如下:
0 10000011 00111010000000000000000
若以double类型存储,原理不变,只是指数位变为11位,对应的存储数据为:移码+1023(210-1)。如十进制数2.5,若以double类型存储,计算过程如下:
- 浮点数:2.5转换为二进制为10.1,浮点表示为1.01×21。
- 符号位:正数,符号位为0。
- 阶码位:阶码为+1,11位指数位的移码偏移为1023,1+1023=1024=10000000000B。
- 尾数位:尾数为1.01,舍去前面的1和小数点,存储为01(之后50个0)。
合在一起,得到2.5在内存中的存储形式如下:
0 10000000000 01(之后50个0)