计算机中是怎么存储浮点数?
计算机中存储浮点数使用的是IEEE 754标准,这是一个国际标准,用于确保在不同的计算机和计算平台之间可以一致地表示和处理浮点数。这个标准定义了几种不同的表示方法,最常用的是单精度(32位)和双精度(64位)浮点数。
一个浮点数由三个部分组成:
-
符号位(Sign bit):这是一个单独的位,用来表示数字的正负。0代表正数,1代表负数。
-
指数位(Exponent bits):这部分用来表示数字的范围(大小)。在32位单精度浮点数中,指数占用8位;在64位双精度浮点数中,指数占用11位。指数使用偏移量或称为偏移二进制表示法,这意味着一个固定的偏移量会被加到实际的指数值上,以允许表示正负指数。
-
尾数位(Mantissa or Significand bits):这部分表示数字的精确度(精度)。它是一个小数部分,通常是以1.xxxx的形式表示,其中1通常是隐含的(不实际存储,但在计算时假定存在)。在32位单精度浮点数中,尾数占用23位;在64位双精度浮点数中,尾数占用52位。
IEEE 754浮点数的实际值是通过这些部分结合起来计算的,公式大致如下:
(
−
1
)
符号位
×
1.
尾数位
×
2
(
指数位
−
偏移量
)
(-1)^{\text{符号位}} \times 1.\text{尾数位} \times 2^{(\text{指数位} - \text{偏移量})}
(−1)符号位×1.尾数位×2(指数位−偏移量)
例如,对于32位单精度浮点数,偏移量是127。如果我们有一个浮点数的指数位表示的值是130(二进制表示),那么真实的指数值是 ( 130 - 127 = 3 ),然后我们会将尾数部分乘以 ( 2^3 ) 来获取实际的数值。
这种表示方法允许在非常宽的范围内表示数字,但也有其限制,尤其是有限的精度。由于尾数位有限,浮点数不能精确地表示所有的小数。这是计算机中使用浮点数时经常需要注意的问题,可能会导致精度损失和舍入误差。
对于32位单精度浮点数,偏移量是127
在32位单精度浮点数中,指数部分使用了8位。由于指数可以是正数或负数,IEEE 754标准使用了一种称为偏移量(或偏移二进制)的表示法来编码指数。这允许指数有一个相对于某个中间值的偏移,这样就可以表示正指数和负指数。
偏移量的计算方法是 (2^{n-1} - 1),其中 (n) 是指数部分的位数。对于32位单精度浮点数,指数部分有8位,因此:
[2^{8-1} - 1 = 2^7 - 1 = 128 - 1 = 127]
这个偏移量确保了指数部分能够以无符号整数的形式存储,并允许表示负指数。指数的实际值是存储值减去偏移量。例如,如果指数部分存储的值是128(二进制为10000000
),实际的指数值是 (128 - 127 = 1)。
使用偏移量127意味着指数的表示范围从-127到+128(不包括0)。这样的范围允许浮点数表示从非常小的数(通过负指数)到非常大的数(通过正指数)。不过,由于特殊的指数值用于表示无穷大和NaN(不是一个数),因此实际的指数范围会稍微小一点。
举例:0.15625 该浮点数中的小数位如何转成二进制
将十进制小数转换为二进制小数的过程称为小数乘2法。我们重复将小数部分乘以2并取整数部分作为二进制的结果,然后用新的小数部分重复这个过程,直到小数部分为0或达到所需的精度为止。这个过程可以用于将小数 0.15625
转换为其二进制等价物。
让我们按步骤来做这个转换:
-
取
0.15625
乘以 2,结果是0.3125
。整数部分是0
,所以二进制的第一位是0
。我们再取结果的小数部分0.3125
。 -
再将
0.3125
乘以 2,结果是0.625
。整数部分是0
,所以二进制的下一位是0
。我们再取结果的小数部分0.625
。 -
将
0.625
乘以 2,结果是1.25
。整数部分是1
,所以二进制的下一位是1
。我们再取结果的小数部分0.25
。 -
将
0.25
乘以 2,结果是0.5
。整数部分是0
,所以二进制的下一位是0
。我们再取结果的小数部分0.5
。 -
最后将
0.5
乘以 2,结果是1.0
。整数部分是1
,所以二进制的下一位是1
。这时小数部分是0
,所以我们可以停止转换。
现在我们将每一步得到的整数部分拼接起来,得到 0.15625
的二进制表示是 0.00101
。
举例:0.15625 该浮点数,在计算机是怎么存储的
将浮点数 0.15625
转换为计算机中的存储形式,我们还是遵循IEEE 754标准,以单精度(32位)格式为例。这个数是正数,所以符号位将是0。以下是详细的步骤:
第一步:转换为二进制
将小数 0.15625
转换为二进制小数:
0.15625
(十进制)= 0.00101
(二进制)
这是通过将小数部分反复乘以2来得到的,如前所述。
第二步:规格化
接下来,我们需要规格化这个二进制数,使其成为 1.xxxxx
的形式,并记录指数的值:
0.00101
规格化为 1.01
× 2^-3
指数是 -3
,因为我们向左移动了三位来得到规格化的数。
第三步:计算指数的二进制表示
接下来,我们将规格化的指数加上偏移量(对于单精度是127)来得到存储值:
指数 + 偏移量 = -3 + 127 = 124
转换为二进制,124是 01111100
。
第四步:确定尾数
取规格化后小数点右边的部分作为尾数,并且不包括隐含的开头的1:
01
(在这里不包括开头的1,因为在IEEE 754标准中它是隐含的)
由于单精度浮点数的尾数需要23位,我们在 01
后面加上零以填充剩余的位数:
0100000...0
(总共23位)
第五步:组合所有部分
最后,我们把所有部分组合起来:
- 符号位:
0
(因为是正数) - 指数位:
01111100
- 尾数位:
01000000000000000000000
(表示小数部分,去掉了隐含的开头的1)
因此,0.15625
的单精度浮点二进制表示为:
0 01111100 01000000000000000000000
举例:245.15625 该浮点数,在计算机是怎么存储的?
要将浮点数 245.15625
存储在计算机中,我们遵循IEEE 754标准,以单精度(32位)格式为例。以下是详细的步骤:
第一步:转换为二进制
首先,将整数部分(245)和小数部分(0.15625)分别转换为二进制。
- 整数部分:
245
转换为二进制是11110101
。 - 小数部分:
0.15625
转换为二进制是0.00101
(如之前例子)。
结合起来,我们得到浮点数的二进制表示:11110101.00101
。
第二步:规格化
接下来,我们将这个二进制数规格化为1.xxxx的形式,同时记录指数的值。
11110101.00101
规格化为 1.111010100101
× 2^7
。
指数是7,因为我们将小数点向左移了7位。
第三步:计算指数的二进制表示
接下来,我们将规格化的指数转换为二进制并加上127(单精度浮点数的偏移量):
指数 + 偏移量 = 7 + 127 = 134。
转换为二进制,134是 10000110
。
第四步:确定尾数
我们现在取规格化后小数点右边的部分作为尾数:
111010100101
(在这里不包括开头的1,因为在IEEE 754标准中它是隐含的)。
由于单精度浮点数的尾数需要23位,我们需要在后面填充额外的0:
11101010010100000000000
(直到达到23位)。
第五步:组合所有部分
最后,我们把所有部分组合起来:
- 符号位(因为是正数,所以是0):
0
- 指数位(134的二进制表示):
10000110
- 尾数位(除了隐含的1以外的部分):
11101010010100000000000
这样,245.15625
的单精度浮点二进制表示为:
0 10000110 11101010010100000000000
为什么浮点数运算的时候会有精度丢失的风险?
浮点数运算时会有精度丢失风险,主要由以下几个原因造成:
-
有限的尾数位数(Mantissa):
IEEE 754标准规定了尾数的位数(例如,单精度为23位,双精度为52位)。这意味着只能存储一定数量的有效数字。如果一个数字需要更多的位数才能准确表示,那么超出尾数部分的信息就会丢失,这就是所谓的“舍入误差”。 -
二进制表示的局限性:
并非所有的十进制小数都能被二进制系统精确表示。例如,十进制中的0.1在二进制中是一个无限循环小数,不能完全精确地表示,必须舍入到最接近的可表示值。 -
运算过程中的累积误差:
当执行多个浮点数运算时,每次运算可能都会引入舍入误差,这些误差会在计算过程中累积,最终可能导致结果与预期相差较大。 -
指数范围的不匹配:
在进行浮点数运算时,如果两个数的指数相差很大,那么在进行加减运算时可能会导致精度损失。小指数的数可能在加到大指数的数时被忽略,因为其贡献太小而无法在尾数中表示。 -
下溢和上溢:
当结果小于可以表示的最小正浮点数时,会发生下溢(underflow),结果可能会被置为0。当结果大于可以表示的最大浮点数时,会发生上溢(overflow),结果可能会被置为无穷大。 -
特定操作的不精确性:
有些数学操作(如求和、求差、乘除和特殊函数)在浮点数上无法精确执行,因为它们的结果可能无法用有限的尾数位精确表示。
由于这些原因,开发者在使用浮点数时需要特别小心,尤其是在需要高精度的财务和科学计算中。在这些情况下,可能需要使用特殊的数学库或数据类型(如Java中的BigDecimal
),以减少舍入误差并提供更精确的结果。
精度丢失举例
让我们用一个简单的例子来说明浮点数运算中可能发生的精度丢失。
假设您有两个十进制小数:0.1
和 0.2
。当您在大多数编程语言中将它们相加时,预期的结果应该是 0.3
。但是,在使用IEEE 754浮点数表示时,因为 0.1
和 0.2
都不能被二进制浮点数精确表示,它们会被近似为最接近的可表示值。这会导致它们相加的结果可能不会精确等于 0.3
,而是一个稍微不同的数字,这就是精度丢失。
在二进制中,0.1
和 0.2
的确切值是无限循环的。因此,它们会被舍入到如下近似值:
0.1
≈0.0001100110011001100110011001100110011001100110011001101
(二进制)0.2
≈0.001100110011001100110011001100110011001100110011001101
(二进制)
当您在计算机中相加这两个数时,舍入误差会传递并可能影响最终结果。因此,当您执行以下运算时:
0.1 + 0.2
您可能得到:
0.30000000000000004
而不是精确的 0.3
。这个额外的 0.00000000000000004
就是由于表示和舍入误差导致的精度丢失。