大家好,今天我们来聊一聊C语言中的Float
类型。
正如标题所说,你知道Float
类型的有效位数有几位吗?
或者你知道为什么Float
类型可以表示数字16777218但是却无法表示16777217吗?
如果你不是很确定那我们就一起来看看吧,有疑问或者想法可以在评论区讨论~
有效位数与科学计数法
理解有效位数和科学计数法是理解Float
精度的关键概念。
这两个概念在二进制与十进制下是一样的,我们所要讨论的Float
有效位数也是针对于十进制而言的。
有效位数是指一个数值中确切有效的数字,即所有可以精确表示的数字。
有效位数的规则:
-
有效位数从第一个非零数字开始。
0.00123 有3个有效位数,因为有效位数从第一个非零的1开始
-
数字中的所有非零数字都是有效的。
123.45 有5个有效位数,因为所有的数字都是确定的
-
小数点右边的所有零如果是有效数字的一部分,则计入有效位数。
100.0 有4个有效位数,因为小数点右边的零是有意义的
-
小数点左边的零不算有效位数。(这条很重要)
100 有1个有效位数
科学计数法的主要目的是简化书写和计算,同时使数值中的有效位数更容易理解。
它的写法如下:
N
=
M
×
1
0
k
N=M×10^k
N=M×10k
其中:
- M 是一个1到10之间的实数,是有效位数的表示。
- k 是一个整数,表示这个数是乘以或除以10的k次幂。
示例:
-
123000 的有效位数为3,用科学计数法表示为:
1.23 × 1 0 5 1.23×10^5 1.23×105 -
0.000456 的有效位数为3,用科学计数法表示为:
4.56 × 1 0 − 4 4.56 \times 10^{-4} 4.56×10−4
Tips:科学计数法中指数部分不影响有效位数
上面两个概念明白以后,我们就可以来分析下Float
类型的有效位数了。
Float
类型在计算机中的表示
Float
类型是单精度浮点数,它在计算机中存储与表示的方法其实就是有效位数+科学计数法。
Float
类型占用4个字节(32位),按照IEEE 754标准的结构,单精度浮点数被划分为三个部分:
-
符号位(sign):1位,表示数值的正负号。
-
指数部分(exponent):8位,用于表示数值的范围,通过偏移量来确定实际指数。
-
尾数部分(mantissa):23位,用于表示数值的有效位,隐含一位1,所以实际上有24位有效数字。
Tips:
对于非零数值,浮点数的二进制表示是以**1.XXXX…**的形式存在的,小数点前的1是隐含的,不需要实际存储。
由于这个1总是存在,因此可以把它省略掉,从而节省1位存储空间。
这就是所谓的隐含的1位。
浮点数表示的形式为:
V
=
(
−
1
)
sign
×
1.
mantissa
×
2
exponent
−
127
V = (-1)^{\text{sign}} \times 1.\text{mantissa} \times 2^{\text{exponent} - 127}
V=(−1)sign×1.mantissa×2exponent−127
Tips:
对单精度浮点数来说,指数部分占8位,其表示范围是0到255。为了表示负指数和正指数的范围,存储的指数实际值需要减去127,称为偏移量。
Float
类型的有效位数
现在我们知道,Float
类型的有效位是由尾数决定的,由于尾数的位数是固定的,因此尾数也就有了最大值的限制。
不难想到,当有效数字超过了尾数所能表示的最大的数时,Float
类型就会丢失精度。
那么尾数所能表示的最大数字是多少呢?
由于有符号位,因此所有尾数都可以用来表示数值,于是24位二进制数能表示的最大值就是:
1111 1111 1111 1111
(有效位数为24位)
转换为十进制就是:16777215
然后我们来看下16777216
、16777217
、16777218
的二进制表示:
-
16777216: 1 0000 0000 0000 0000
有效数字为1位,小于24位
-
16777217: 1 0000 0000 0000 0001
有效数字为25位,大于24位
-
16777218: 1 0000 0000 0000 0010
有效数字为24位,等于24位
看到这里,大家应该明白为什么Float
类型可以表示16777218
,却不能表示16777217
了。
所以,当数值不超过16777216
时,Float
类型可以精确表示每一个整数;
当数值超过16777216
时,Float
类型不能再精确表示所有的数值,精度开始丢失。
因此,float类型的有效位数是7位(因为7位数是一定可以精确表示的)。
最后,我们可以通过一个简单的C程序来演示上述现象:
#include <stdio.h>
int main()
{
float num1 = 16777216.0f; // 可以表示的数
float num2 = 16777217.0f; // 超出精度的数
float num3 = 16777218.0f; // 可以表示的数
printf("num1 = %.0f\n", num1); // 输出:16777216
printf("num2 = %.0f\n", num2); // 输出:16777216
printf("num3 = %.0f\n", num3); // 输出:16777218
return 0;
}
输出结果:
num1 = 16777216
num2 = 16777216
num3 = 16777218
可以看到,16777217 无法被精确表示,而 16777218 则可以。
希望本文对大家有帮助,最后欢迎大家关注我的微信公众号《嵌入式三分钟》