1.浮点数的概念
浮点数是一种用于表示实数(即带有小数部分)的数学类型。在计算机中,浮点数通常由一个符号位、一个指数位和一个尾数位组成。
1.1 浮点数的格式
IEEE 754标准规定了浮点数的表示方法。它将浮点数划分为单精度浮点数(32位)和双精度浮点数(64位),其中单精度浮点数由32位存储,采用以下格式:
31 0
+-+-----------+
|S| Exponent| Mantissa
+-+-----------+
其中,S表示符号位,Exponent表示指数部分,Mantissa表示尾数部分。
双精度浮点数由64位存储,采用以下格式:
63 0
+-+--------------+
|S| Exponent | Mantissa
+-+--------------+
同样地,S表示符号位,Exponent表示指数部分,Mantissa表示尾数部分。
2. C编译器中的浮点数存储方式
C编译器会将浮点数转换为机器语言。在C语言中,float和double是分别用来定义单精度和双精度浮点数的关键字。
2.1 单精度浮点数的存储方式
单精度浮点数由32位存储,其中1位表示符号位,8位表示指数部分,23位表示尾数部分。
在内存中,单精度浮点数采用小端(little-endian)方式存储,也就是说,字节的低位存储在内存的低地址处,字节的高位存储在内存的高地址处。
下面是一个用C语言声明变量并赋初值的例子:
float f = 1.23;
可以使用指针来查看这个变量在内存中的存储情况:
#include <stdio.h>
int main() {
float f = 1.23;
printf("%X\n", *((unsigned int*)&f));
return 0;
}
运行结果为:
3F9DF5C3
可以看到,1.23在内存中的存储方式为3F9DF5C3。
2.2 双精度浮点数的存储方式
双精度浮点数由64位存储,其中1位表示符号位,11位表示指数部分,52位表示尾数部分。
与单精度浮点数类似,双精度浮点数在内存中也采用小端方式存储。
下面是一个用C语言声明变量并赋初值的例子:
double d = 1.23;
可以使用指针来查看这个变量在内存中的存储情况:
#include <stdio.h>
int main() {
double d = 1.23;
printf("%llX\n", *((unsigned long long*)&d));
return 0;
}
运行结果为:
3FF3C083126E978D
可以看到,1.23在内存中的存储方式为3FF3C083126E978D。
3. 浮点数运算中的精度问题
浮点数的存储方式决定了它们在计算中可能会出现精度问题。这是因为在计算机中,浮点数是以二进制形式存储的,而大多数实数无法精确地用有限个二进制位来表示。
3.1 浮点数的舍入误差
在进行浮点数运算时,会出现舍入误差(round-off error),也就是说,在进行计算的过程中,有些位被舍弃了,从而导致了一定的误差。这种误差在多次运算时会逐渐累积。
例如:
#include <stdio.h>
int main() {
float f = 0.1;
float sum = 0;
for (int i = 0; i < 10; i++) {
sum += f;
}
printf("%f\n", sum);
return 0;
}
运行结果为:
0.999999
可以看到,计算结果不是1,而是0.999999,这是因为在10次运算中,每次都会出现舍入误差。
3.2 计算机浮点数存储的精度范围
由于存储精度的限制,计算机可以表示的浮点数的范围是有限的。对于单精度浮点数,它的最大值是3.4028235e+38,最小非零值是1.17549435e-38。对于双精度浮点数,它的最大值是1.7976931348623157e+308,最小非零值是2.2250738585072014e-308。
如果一个浮点数的大小超过了这个范围,就可能会出现溢出或下溢的问题。
4. 总结
本文介绍了浮点数的概念和C语言中浮点数在内存中的存储方式。由于计算机采用的是二进制存储方式,浮点数在计算中可能出现的精度问题也会在一定程度上影响计算结果的准确性。为了避免这种问题,我们需要在编写程序时特别关注浮点数的计算方式,并尽可能采用适当的算法来减小误差。