C 语言 printf 函数以 double 输出 int 类型数据问题
前言
最近在社团出招新试题,朋友出了一道 C 语言题,我在帮写答案的时候发现这个题目有点意思。
题目如下:
1 | int main(){ |
输出:
1 | -1 |
解释出现这种情况的原因?
答:
%f
是把数据以 double
格式打印出来的,int
和 double
的格式不一样。不过,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。
对于 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
格式打印出来的,int
和double
的格式不一样。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
多的位置。
Author: xun
Link: http://blog.fooo.in/2022/10/17/computer-science/c-printf-int-double/
License:
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。