• 相信大多数人知道整形数在内存中的分布方式,而且也能很容易写出其二进制的形式,但是对于浮点数,估计知道的人并不是很多
  • 今天学习在C语言中浮点数在内存中的表示方法

1 浮点数在内存中的存储方式

  • 浮点数在内存中的存储方式为:符号位+指数+尾数。对于float与double类型的存储方式,如下图所示:

  • float与double类型的数据在计算机内部的表示法是相同的,但是由于所占存储空间的不同,其分别能表示的数值范围和精度不同。

上面的表示法,可能并不是很好理解,看到后面的例子就会恍然大悟。

1.1 浮点数的转换步骤

具体如何将浮点数转换成内存中的二进制(或者十六进制的形式),下面就来看看转换步骤。

  1. 首先将浮点数转换成二进制形式(这个自己查资料学习如何转换,不是本文的目的)
  2. 用科学计数法表示二进制浮点数
  3. 计算指数偏移后的值
  • 注意,第三点需要明白,计算指数时需要加上偏移量(这里不讲解为什么存在偏移量,后序文章会学习),对于float与double类型,加上的偏移量是不一样的。具体如下:

对于指数为6,偏移后的值为:

float:6+127 ---->> 133
double : 6+1023 ---->> 1029

对于上面的内容,目前可能还没有理清。但是下面的例子,可以将上面的所有内容学习明白

1.2 浮点数的转换实际例子分析

上面说了一堆,这里来一个例子彻底明白上述的转换规则。

  • 实数8.25在内存中的float表示为如下:
  1. 首先是确定8.25的二进制表示形式:1000.01

  2. 然后用科学技术法表示二进制浮点数:1.00001 * (23).

  3. 计算符号位:3+127 = 130 ----> 10000010

  4. 符号位为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所表示的数不是连续的数