#问题

1
2
3
4
5
6
7
8
#include <stdio.h>

int main()
{
float a = 123456789;
printf("%f", a);
return 0;
}

上述代码的输出结果为:123456792.000000,为什么?

#float存储方法

详细内容可以参考这篇博客,float型的数据占用32位的存储空间,其中:

  • 1位符号位
  • 8位指数位
  • 23位尾数位

float型数据的存储方法为科学计数法,即 110 可以写成 + 1.1 * 10^2 的形式,即:

  • + 为符号位
  • 2 为指数位 2次幂
  • 1 为尾数位 .1 而不是 1.1

这里注意一下,因为科学计数法是 一个大于1小于等于9 的小数(相对于十进制),二进制的科学计数法的小数的整数部分一定是 1,故省略整数部分,故实际上尾数的有效位数是 24 位。

#数字分析

使用进制转换工具得到 123456789 的二进制为

1
111010110111100110100010101

一共 27 位,前面说过float的有效位数为 23+1 位,第 24 位要根据 就近原则[1] 确定是 0 还是 1,其余部位补 0,即:

1
111010110111100110100011000

得到结果为 123456792

#程序验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <stdlib.h>
/*
num是要展示的float数,bin是保存所有二进制位的数组
*/
void getFloatBin(float num, char bin[])
{
int t = 1; //用来按位与操作
int *f = (int *)(&num); //将float的解释成int,即float的地址转成int*
for (int i = 0; i < 32; i++)
{
//从最高位开始按位与,如果为1,则bin[i]=1,如果为0,则bin[i]=0
//这里没有将bin存成字符,而是数字1 0
bin[i] = (*f) & (t << 31 - i) ? 1 : 0;
}
}
int main()
{
float test = 100;
char c[32];
printf("测试float数为:%f\n", test);
printf("二进制表示为:");
getFloatBin(test, c);
for (int i = 0; i < 32; i++)
{
printf("%d", c[i]);
if (i == 0)
printf(", ");
if (i == 8)
printf(", ");
}
}

以上代码摘自在C语言中将float(浮点数)的二进制表示打印出来,结果为:

1
2
测试float数为:123456792.000000
二进制表示为:0, 10011001, 11010110111100110100011

与理论结果比对一致。

#结语

感谢舍友和老辣鸡们的群策群力!最终初步解决了问题~

#参考文档


  1. ”就近原则“无法找到相关文档说明,只能根据测试数据进行推测,准确性有待考究。 ↩︎