浮点数的存储
浮点型变量在计算机内存中占用4个字节(4 Byte),即32-bit,一个浮点数由3部分组成:符号位、指数 和 底数(尾数);
可以理解为浮点数是用科学计数法来存储数据,如
−
1.51
×
1
0
−
5
-1.51 \times 10^{-5}
−1.51×10−5
符号位:0=正数;1=负数;占用最高位1bit
指数部分:表示10的X次幂,即例子中的-5。占用 8bit (11bit) 空间来表示,8bit 表示数值范围:0-254 ( 0 和 255 被保留用于表示特殊值);为了能表示负数,正负各一半,采用移位存储方式,实际值需要减去127,所以指数可从 -126到+127.。
特例:浮点数 为0时,指数和底数都为0,但此前的公式不成立。因为2的0次方为1,所以,0是个特例。这个特例也不用认为去干扰,编译器会自动去识别。
底数部分:表示此浮点数的实际值,使用2进制数来表示;占用23bit,因为二进制第一位一定是1,所以可以省略,所以实际可以表示24bit的数。
二进制转十进制浮点数:
看下-12.5在计算机中存储的具体数据:0xC1 0x48 0x00 0x00
二进制:1100 0001 0100 1000 0000 0000 0000 0000
格式: SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
S: 为1,是个负数。
E:(8-bit)为 10000010 转为10进制为130,130-127=3,即实际指数部分为3.
M:(23-bit)为 10010000000000000000000。需要加上被省略的第一位1底数实际上是:1.10010000000000000000000(二进制)
实际值
=
S
×
M
×
1
0
E
实际值 = S \times M \times 10^E
实际值=S×M×10E
通过指数部分E的值来调整底数部分M的值
调整方法为:如果指数E为负数,底数的小数点向左移,如果指数E为正数,底数的小数点向右移。小数点移动的位数由指数E的绝对值决定。这里,E为正3,使用向右移3为即得: 1100.10000000000000000000 二进制转十进制
小数点左边的1100 表示为$ (1 × 2^3) + (1 × 2^2) + (0 × 2^1) + (0 × 2^0)$, 其结果为12 。小数点右边的 .100… 表示为 $(1 × 2^{-1}) + (0 × 2^{-2}) + (0 × 2^{-3}) + … $,其结果为.5 。以上二值的和为12.5 符号位计算
由于S 为1,使用为负数,即-12.5
十进制浮点数转为二进制:
下面看下如何将一浮点数装换成计算机存储格式中的二进制数。 举例将19.625换算成 float型。
转为二进制
整数部分采用 “除2取余,逆序排列” 10011
19 / 2 = 9...1
9 / 2 = 4...1
4 / 2 = 2...0
2 / 2 = 1...0
1 / 2 = 0...1
小数部分采用 “乘2取整,顺序排列” 101
0.625 * 2 = 1.250 取1
0.250 * 2 = 0.500 取0
0.500 * 2 = 1.0 取1
合并得到 10011.101
底数部分移位,使底数变成1.xxxx
小数点左移4位,变成1.0001101这样底数为:1.0001101指数为:4+127=131,二进制为:1000011 符号位为0,因为是正数;
合并
初步合并得到0 1000011 0001101后面补0,补成32-bit,得到 0100 0011 0001 1010 0000 0000 0000 0000
精度丢失
精度丢失是计算机中浮点数无法精确表示某些十进制数时导致的误差现象
主要原因是 小数部分的计算方式(乘2取整)存在循环的问题,而浮点数的底数部分位数有限,因此无限循环的二进制小数会被截断,存储为近似值。
例如:十进制0.1 → 二进制0.0001100110011...(无限循环截取23位或52位)
0.1 * 2 = 0.2 取0
0.2 * 2 = 0.4 取0
0.4 * 2 = 0.8 取0
0.8 * 2 = 1.6 取1
0.6 * 2 = 1.2 取1
0.2 * 2 = 0.4 取0
...无限循环
double sum = 0;
for (int i = 0; i < 10; i++) sum += 0.1; // 结果可能为0.9999999999999999而非1.0
解决方案
定点数替代:对货币等场景,使用整数表示最小单位(如分)避免浮点运算。高精度类型:使用BigDecimal(Java)、decimal(C#/Python)等十进制类型。误差容忍比较:比较浮点数时允许微小误差(如 Math.abs(a - b) < 1e-10)。
参考文献
推荐阅读 - c语言中float、double、long double在内存中存储方式