首先要了解一下下面的知识:
[-3.4028235E38, 3.4028235E38]
,精确范围是[-340282346638528859811704183484516925440, 340282346638528859811704183484516925440]
[-2147483648, 2147483647]
,一共是
2
32
=
4294967296
2^{32}=4294967296
232=4294967296 种可能,为什么 float 既能表示小数,并且其表示范围还那么大呢?因为间隔!int 的间隔固定为1,float 的间隔是不是等间距的,它在 0 的附近间距小,在远离 0 的位置间距大(实际上,因为某些原因,float 能精确表示的数还不足
2
32
=
4294967296
2^{32}=4294967296
232=4294967296 种1)。4294967244.001
和 4294967295.009
均用 4294967296.000000
表示!7
,这句话的含义是 float 至少保证 7 位有效数字是准确的!float
在计算机中一般是用 4 个字节进行存储的,也就是 32 位,其中第 1
位为符号位,第 2-9
位(共 8 位)用来表示指数,第 10-32
(共 23 位)用来表示尾数。下图形象地表示了 float
各个位发挥的作用:
符号位很好理解,就是用 0
表示正数,用 1
表示负数;那么指数和尾数怎么理解呢?
首先我们知道常用的十进制科学计数法是将所有的数字转换成
(
±
)
a
.
b
×
1
0
c
(±)a.b \times 10^c
(±)a.b×10c 的形式,其中 a
的范围是 1-9
共 9 个整数,b 是小数点后的所有数字,c 是 10 的指数。而计算机中存储的都是二进制数据,所以 float 存储的数字都要先转化成
(
±
)
a
.
b
×
2
c
(±)a.b \times 2^c
(±)a.b×2c,由于二进制中最大的数字就是1,所以表示法可以写成
(
±
)
1.
b
×
2
c
(±)1.b \times 2^c
(±)1.b×2c 的形式,float 要想存储小数就只需要存储 (±),b 和 c 就可以了。
所以上面将 32 位划分成了三块,第一块存储符号,第二块存储指数 c
,第三块存储指数 b
!
举个例子,我们来表示8.25
1000
;0.25*2 = 0.5
,整数部分为0
,记下:0;0.5*2 = 1.0
,整数部分为1,记下:1,所以 0.25
的二进制表示即为 0.01
(1*2^{-2}
)8.25
的(伪)二进制表示为1000.01
1000.01=1.00001*2^3
8.25
的二进制表示了。因为8.25
是正数,所以符号位为0
;指数为3(3+127=130)
,所以1000 0010
(130的二进制表示);小数位为 00001
,因为要用 23 位来表示它,所以需后补 0,所以0000 1000 0000 0000 0000 000
8.25
的二进制表示为0 1000 0010 0000 1000 0000 0000 0000 000
下面是一个大佬用 C 语言写的一个验证代码2:
#include
int main()
{
float a = 1;
unsigned int s, e, i;
char b[33], c, d;
while (a)
{
printf("输入一个浮点数(0退出):");
scanf("%f", &a);
s = e = *((int *)&a); // 强制类型转换到无符号整数
b[32] = 0;
for (i = 32; i; i--, s /= 2)
b[i - 1] = s % 2 + '0'; // 十进制二进制转换
printf("内存:%f=%s\n", a, b);
printf("符号:%c\n", b[0]);
c = b[9];
b[9] = 0;
d = ((e & 0x7f800000) >> 23) - 127;
printf("指数:%s (浮点移动位数%d,>0右移,<0左移)\n", b + 1, d);
b[9] = c;
printf("尾数:%s (不含最前面隐含1)\n小数:", b + 9);
d++;
b[8] = '1';
for (i = 31; i > 9 && b[i] == '0'; i--)
;
b[i + 1] = 0; // 消尾0
if (b[0] == '1')
printf("-"); // 负数
if (d > 0) // 小数点浮动
{ // 小数点右移
for (i = 0; i < d; i++)
printf("%c", b[i + 8]);
printf(".%s (手工转换为十进制方法是:小数点前乘2,小数点后除以2)\n\n", b + i + 8);
}
else
{ // 小数点左移
printf("0.");
for (; d; d++)
printf("0");
printf("%s (手工转换为十进制方法是:小数点前乘2,小数点后除以2)\n\n", b + 8);
}
}
}
下面是验证结果: