由一道程序员面试宝典引出的问题-printf如何读入浮点数!

在讲这个题目之前,预备知识,讲一下,printf函数,输入参数是读入缓冲区保存,再按照%?的格式从缓冲区中读出数据,并据此格式解释数据。

有了这个知识之后,在讲程序员面试宝典上看到一个题:

view plaincopy to clipboardprint?
#include "stdio.h"  
int main(int argc, char* argv[])  
{  
    printf("%d/n",5.01);  
    return 0;  
}  
输出结果为:188978561 
#include "stdio.h"
int main(int argc, char* argv[])
{
 printf("%d/n",5.01);
 return 0;
}
输出结果为:188978561

然后开始研究为什么会是这个数?

5.01是double类型,内存中占8个字节,保存在缓冲区。而%d为整型,占4个字节,printf从缓冲区中读入4字节,先读到低32位的数据。也就是说printf输出的应该是5.01以double类型保存数剧的低32位。为了检验此结果是否正确,对比5.01在内存中的表示与输出。

view plaincopy to clipboardprint?
#include "stdio.h"  
int main(int argc, char* argv[])  
{  
    double d = 5.01;  
    int *p = (int *)(&d);  
    int rst = 1889785610;  
    printf("1).%x/n",*p);  
    printf("2).%x/n",*(p+1));  
    printf("3).%x/n",rst);  
    return 0;  
}  
输出为:  
1).0x70a3d70a  
2).0x40140a3d  
3).0x70a3d70a 
#include "stdio.h"
int main(int argc, char* argv[])
{
 double d = 5.01;
 int *p = (int *)(&d);
 int rst = 1889785610;
 printf("1).%x/n",*p);
 printf("2).%x/n",*(p+1));
 printf("3).%x/n",rst);
 return 0;
}
输出为:
1).0x70a3d70a
2).0x40140a3d
3).0x70a3d70a

这也就证明了%d输出了5.01的低32低。5.01的double类型,在内存的的表示为0x40140a3d70a3d70a。

事情看似也就完成了。

我又想,如果输入是浮点类型的5.01f,又会发生什么呢?

view plaincopy to clipboardprint?
#include "stdio.h"  
int main(int argc, char* argv[])  
{  
    float f = 5.01f;    
    int *p = (int *)(&f);       
    printf("1).0x%x/n",*p);    
    printf("2).0x%x/n",5.01f);    
    return 0;    
}  
输出:  
1).0x40a051ec  
2).0x80000000 
#include "stdio.h"
int main(int argc, char* argv[])
{
    float f = 5.01f; 
    int *p = (int *)(&f);    
    printf("1).0x%x/n",*p); 
    printf("2).0x%x/n",5.01f); 
    return 0; 
}
输出:
1).0x40a051ec
2).0x80000000

我们发现,此时输出的并不是浮点类型5.01f的内存的表示,这是为什么呢?

然后看到一个说法,是printf会把%f按double类型输出,也就是说会把参数float型的转成double型在输出。

但现在并不是%f,当然用%f显示的是正确的结果。于是我猜测,printf是将所在float型读入的数据都自动的转化为double型了,然后%f就按double处理,而我们这是%d,所以显示的为float转化为double型后的低4字节。

验证此想法:

view plaincopy to clipboardprint?
#include "stdio.h"  
int main(int argc, char* argv[])  
{  
    double f = 5.01;    
    int *p = (int *)(&f);  
    printf("1).0x%x/n",*p);  
    printf("2).0x%x/n",*(p+1));    
    printf("3).0x%x/n",5.01f);    
    return 0;    
}  
输出:  
1).0x70a3d70a  
2).0x40140a3d  
3).0x80000000 
#include "stdio.h"
int main(int argc, char* argv[])
{
    double f = 5.01; 
    int *p = (int *)(&f);
    printf("1).0x%x/n",*p);
    printf("2).0x%x/n",*(p+1)); 
    printf("3).0x%x/n",5.01f);  
    return 0; 
}
输出:
1).0x70a3d70a
2).0x40140a3d
3).0x80000000

但是我们发现结果并不一样,于是我又猜想,也是许printf将float转化为double的方式与默认的方式不一样

5.01d的默认的表示为:0x40140a3d70a3d70a,在上面已经说明了

view plaincopy to clipboardprint?
#include "stdio.h"  
int main(int argc, char* argv[])  
{  
    printf("0x%8x/n0x%8x/n",5.01f);  
    return 0;  
}  
输出为:  
0x80000000  
0x40140a3d 
#include "stdio.h"
int main(int argc, char* argv[])
{
 printf("0x%8x/n0x%8x/n",5.01f);
 return 0;
}
输出为:
0x80000000
0x40140a3d

与是发现printf将5.01f->5.01d的表示是:0x40140a3d80000000

接着就是看这两个值是否都是为5.01了:

view plaincopy to clipboardprint?
#include "stdio.h"  
int main(int argc, char* argv[])  
{  
    int d1[2], d2[2];  
    d1[0]=0x80000000;  
    d1[1]=0x40140a3d;  
    d2[0]=0x70a3d70a;  
    d2[1]=0x40140a3d;  
    double *p1 = (double *)d1;  
    double *p2 = (double *)d2;  
    printf("1).%f/n",*p1);  
    printf("2).%f/n",*p2);  
    return 0;    
}  
输出为:  
1).5.010000  
2).5.010000 
#include "stdio.h"
int main(int argc, char* argv[])
{
 int d1[2], d2[2];
 d1[0]=0x80000000;
 d1[1]=0x40140a3d;
 d2[0]=0x70a3d70a;
 d2[1]=0x40140a3d;
 double *p1 = (double *)d1;
 double *p2 = (double *)d2;
 printf("1).%f/n",*p1);
 printf("2).%f/n",*p2);
    return 0; 
}
输出为:
1).5.010000
2).5.010000

也就证明了0x40140a3d80000000,与0x40140a3d70a3d70a都是5.01d在机器中的表示。前者为5.01f(0x40a051ec)由printf转化为double后的表示,后者为5.01d的默认的表示。

总结:printf将输的浮点型参数全都自动转化为双精度型,且与默认的双精度的表示方法是不同的。最重要一点,printf不安全,类型不安全,要是类型不对了,也许我们就挂了^_^

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tiansilv/archive/2010/07/17/5742645.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值