前言

最近在社团出招新试题,朋友出了一道 C 语言题,我在帮写答案的时候发现这个题目有点意思。

题目如下:

1
2
3
4
int main(){
int i=1;
printf("%f\n",i);
}

输出:

1
-1

解释出现这种情况的原因?

%f 是把数据以 double 格式打印出来的,intdouble 的格式不一样。不过,C99 之后可以也可以使用 %lf 来输出 double

int 是准确值,而 double 是精确值,准确转精确会精度丢失。int 的存储结构是:一个符号位 31 个指数位。double 的存储结构是:1 个符号位,11个指数位,52 个尾数。格式化字符串不会帮你做类型转换,并不会智能的将 int 转为 double,它会按照计算机内部的数据表示忠实反映结果, 只是将 1 在内存中的二进制形式按照 double 存储标准来解析。

%f 寻 64 位内存,int 只有 32 位,所以会在后面全补 0 直到补满 64 位。

对于 64 位的浮点数,最高的 1 位是符号位 S,接着的11位是指数 E,剩下的 52 位为有效数字 M。

img

对于 32 位的浮点数,最高的 1 位是符号位 s,接着的 8 位是指数 E,剩下的 23 位为有效数字 M。

·

根据国际标准 IEEE 754,任意一个二进制浮点数 V 可以表示成下面的形式:

$V=(-1)^{s}M2^{E}$

  • $(-1) ^ s$ 表示符号位,当 s=0,V 为正数;当 s=1,V 为负数。

  • M 表示有效数字,大于等于 1,小于 2。

  • $2^E$ 表示指数位。

%f 是把数据以 double 格式打印出来的,intdouble 的格式不一样。

1 ≤ M < 2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。

指数 E 还可以再分成三种情况:

**(1)e 不全为 0 或不全为 1。**这时,浮点数就采用上面的规则表示,即指数E的计算值减去 127(或 1023,这是在 double 的情况下,下同),得到真实值,再将有效数字M前加上第一位的1。

**(2)e 全为 0。**这时,浮点数的指数 E 等于 1-127(或者 1-1023),有效数字 M 不再加上第一位的 1,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ±0,以及接近于 0 的很小的数字。

**(3)e 全为 1。**这时,如果有效数字 M 全为 0,表示±无穷大(正负取决于符号位 s);如果有效数字 M 不全为0,表示这个数不是一个数(NaN)。

这题符合第二个规则,所以最终公式:

$V=(-1)^0*2^{-51}*2^{-1022}=2^{-1073}$

$M = $

$2^{-1073}$ 约等于 $10^{-322}$

验证:出现第一个非零的数确实是在 300 多的位置。

image-20220922175436498