热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C语言printf%d输出float、double类型的值出现的问题

C语言printf%d输出float、double类型的值出现的问题例如有下面几行代码:#include<stdio.h>intmain(){printf

C语言 printf %d 输出float、double类型的值出现的问题

例如有下面几行代码:

#include
int main()
{
printf("%f\n",5);
printf("%d\n",5.01);
printf("%f\n", (float)5);
printf("%f\n", 5.f);
return 0;
}

它的输出结果是:

这里写图片描述

看到结果,会感觉非常奇怪。1处怎么会输出0呢?2又为何会显示这么大的一个数呢?

解释:

下面是转自网上的一篇博客的解释
1,之所以没输出5,这是C语言设计的原因。
2,之所以输出0,这是计算机体系结构的问题。

具体来说:
printf函数不会进行任何类型转换,它只是从内存中读出你所提供的元素的值(按照%d,%f等控制字符提示的格式)。C语言设计中,int类型一般是32bit或者16bit,而float一般是64bit,并且有可能使用科学计数保存。5在内存中为00000000,00000101。而且5一般都在静态区,程序的静态存储区默认是0,那么当用%f来读时,就会读64bit,也就是会读之前的很多位0,最后按照(有效数字)×(基数2)pow(指数)的方式来取数,自然结果是0
之所以Vc中不允许这种情况,而有些编译器就允许这么输出就是编译器设置的问题。按理说,这样访问内存是属于越界访问,应该禁止。不过只是读,伤害性不大而已。 对于单精度浮点数(32bit),不少c语言编译系统以24位表示小数部分(包括1bit符号位),以8位表示指数部分。
首先注意(栈中的数字是从高位向地位存储的!比如 int a=3;&a指的是低位地址 不是高位地址,即是int的末尾 而不是int的开头位置。)
其实,在我们当我们弄清楚浮点数的存储格式之后,就会明白其中的奥妙了。 在C语言标准中,浮点数是采用IEEE754标准 float类型数据存储格式如下:
最高位最低位符号S 阶码E 尾数M 最高位 31 位 ,保存符号位 S“, 0”表示正数 ,“1”表示负数 30 位~23 位 ,共 8 位 ,移码方式(指数值加上偏移量127)保存指数部分 ,称为阶码 22 位~0 位 ,共 23 位 ,保存系数部分 ,称为尾数 ,对于规范化二进制数 ,整数位的前导“1”不保存 。
隐含 直接保存小数部分 对于 double 型 ,IEEE 754 - 1985标准规定用64 位表示 ,具体如下: ·最高位63 位 ,保存 S“, 0”正数“, 1”负数; ·62 位~52 位 ,共 11 位 ,移码方式(指数值加1023)保存指数 ,称为阶码;·51 位~0 位 ,共 52 位 ,保存系数部分 ,称为尾数 ,对于规范化二进制数 ,整数位的“1”不保存隐含) ,直接保存小数部分 将5.01压入栈,为double的数,栈8个字节。
对于 printf(“%f”,5) 首先5是int型的,在内存中占有4B,而输出确要是8B个字节,5在内存中为0000 0000 0000 0000 0000 0000 0000 0101 按%f输出还必须加入0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0101 才行,由于double类小数点后面只保留6位,所以不可能取到有非0的101处,所以答案为0.000000。
==========================
printf(“%d\n”,5.01); 为什么输出一个大数?
在讲这个题目之前,预备知识,讲一下,printf函数,输入参数是读入缓冲区保存,再按照%?的格式从缓冲区中读出数据,并据此格式解释数据。
有了这个知识之后,在讲程序员面试宝典上看到一个题:

#include "stdio.h" 
int main()
{
printf("%d\n",5.01);
return 0;
}

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

#include "stdio.h"
int main()
{
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,又会发生什么呢?

#include "stdio.h" 
int main()
{
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字节。
验证此想法:

#include "stdio.h" 
int main()
{
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,在上面已经说明了

#include "stdio.h" 
int main()
{
printf("0x%8x\n0x%8x\n",5.01f);
return 0;
}

输出为:

0x80000000   
0x40140a3d

与是发现printf将5.01f->5.01d的表示是:0x40140a3d80000000
接着就是看这两个值是否都是为5.01了:

#include "stdio.h" 
int main()
{
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不安全,类型不安全,要是类型不对了,也许我们就挂了^_^

通过以上解释,我们大致明白:
1. printf输出float型时,会自动转化成double型;
2. 由于存储时,都是先低位,再高位,同时经过转化成double,前面会取很多0(越界访问);
3. 5.01,打印时按照int来取,只取前四个字节。

本文转自:1、http://blog.sina.com.cn/s/blog_6f611c300101eil2.html
2、http://blog.csdn.net/fengyunjh/article/details/6230164


推荐阅读
author-avatar
wangxin7299b_943
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有