- 相信大多数人知道整形数在内存中的分布方式,而且也能很容易写出其二进制的形式,但是对于浮点数,估计知道的人并不是很多
- 今天学习在C语言中浮点数在内存中的表示方法
文章目录
1 浮点数在内存中的存储方式
- 浮点数在内存中的存储方式为:符号位+指数+尾数。对于float与double类型的存储方式,如下图所示:
- float与double类型的数据在计算机内部的表示法是相同的,但是由于所占存储空间的不同,其分别能表示的数值范围和精度不同。
上面的表示法,可能并不是很好理解,看到后面的例子就会恍然大悟。
1.1 浮点数的转换步骤
具体如何将浮点数转换成内存中的二进制(或者十六进制的形式),下面就来看看转换步骤。
- 首先将浮点数转换成二进制形式(这个自己查资料学习如何转换,不是本文的目的)
- 用科学计数法表示二进制浮点数
- 计算指数偏移后的值
- 注意,第三点需要明白,计算指数时需要加上偏移量(这里不讲解为什么存在偏移量,后序文章会学习),对于float与double类型,加上的偏移量是不一样的。具体如下:
对于指数为6,偏移后的值为:
float:6+127 ---->> 133
double : 6+1023 ---->> 1029
对于上面的内容,目前可能还没有理清。但是下面的例子,可以将上面的所有内容学习明白
1.2 浮点数的转换实际例子分析
上面说了一堆,这里来一个例子彻底明白上述的转换规则。
- 实数8.25在内存中的float表示为如下:
-
首先是确定8.25的二进制表示形式:1000.01
-
然后用科学技术法表示二进制浮点数:1.00001 * (23).
-
计算符号位:3+127 = 130 ----> 10000010
-
符号位为0, 10000010 , 小数为(尾数):00001
由上面4条的计算以及前面的规则,得到8.25的float表示为:
- 0 10000010 00001000000000000000000 ----> 0x41040000
至此,已经得出了8.25的浮点数表示形式。对于double的表示形式,可以自己进行推导。
1.3 编程验证测试
对于1.2小节的内容,可以编写下面的程序来进行验证:
#include <stdio.h>
int main()
{
float f = 8.25;
unsigned int* p = (unsigned int*)&f;
printf("0x%08X\n", *p);
return 0;
}
运行结果也是等于: 0x41040000 可以验证上述规则与推导的正确性。
2 int与float类型的范围的比较
我们知道int与float都是占用4字节内存,但是他们所能够表示的数的范围并不是一样的。如下图:
- int 类型的范围:[-231 , 231]
- float类型的范围:[-3.4 * 1038 , 3.4 * 1038]
首先抛开float为什么是这个范围不说(具体的原因本文不讲)。为什么int和float同样是占用4字节的内存,但是float所表示的数的范围明显比int的范围大很多?
其实只要占用的字节数相同,那么所能表示的数的个数,就一定相等,比如一字节的内存就最多能表示 256个数字。但是有符号和无符号表示的数范围就不一样。注意,是数范围不一样,但是他们都只能表示256个数字。只是这256个数字的范围不在同一个区间。
那么float也是一样,它也是只能表示与int类型一样多的数字,只是float表示的数的范围是上面的那样而已。它能表示的数的范围很大,不要被这一点迷惑。
- 可能我们觉得[-3.4 * 1038 , 3.4 * 1038] 这个区间很大,它的个数比int表示的个数多很多。 注意,这个想法是错误的!!!
- float所表示的数不是连续的数,在[-3.4 * 1038 , 3.4 * 1038]这个区间,存在很多空洞,只有少部分是表示float的数。
- float只是一种近似的表示法,不能作为精确数使用。
当然这一切,都是由于float的内存表示法的不同造成的。有兴趣的人可以研究一下,上述float的数的范围区间是如何来的。
2.1 float数不精确的编码案例
看下面的代码:
#include <stdio.h>
int main()
{
float f = 3.1415f;
float fl = 123456789;
printf("%0.10f\n", f);
printf("%0.10f\n", fl);
return 0;
}
编译运行结果为:
3.1414999962
123456792.0000000000
这个结果貌似与我们所期盼的结果有很大的出入。但是 这就是正常的结果,因为float表示的数是不精确的,像上面的数3.1415f ,float就无法表示这样的数,虽然它在区间[-3.4 * 1038 , 3.4 * 1038]中,但是float就是无法精确表示它,只能打印它最接近这个数的一个数。所以打印结果是另一个数。
- 这也验证了上面关于说float无法精确表示一个数的说法
3 总结
- 浮点数与整形数的内存表示法不同
- 浮点数类型可表示的范围更大
- 浮点类型是一种不精确的类型
- 由于浮点数的内存表示法比较复杂,所以浮点数的运算速度很慢
- float所表示的数不是连续的数