您的位置:首页 > 其它

printf格式化输出中要输出的格式与输入的变量格式不统一的问题

2013-08-08 16:39 190 查看
那是在学习浮点数的一个精度问题的时候遇到的事情。

本来想把单精度浮点数打印出来看看内存存放情况的,结果发现了如下问题:

float f = 1.0f;

printf("f = 0x%08X, 1.0 = 0x%08X", f, 1.0f);

用gcc编译好后连续运行了几遍发现结果很奇怪

f = 0xC35E8D28, 1.0 = 0xC35E8D38

f = 0xE028AEF8, 1.0 = 0xE028AF08

f = 0xF329C0B8, 1.0 = 0xF329C0C8

至于这个怎么解释我目前也说不清楚,下面我就说说我能搜到的信息和我的总结。

首先是用%08X打印的初衷是希望能够像其他32位数据结构一样打印出float的内存存储情况。

因为我以为float的内存存储情况就是下面写的情况

符号位     阶码    尾数     长度
float        1         8      23       32
double       1         11     52       64
但是实际上当变量f在printf中被用于打印时根本不是这样的进栈的。

我在网上找到了一个同学做的如下实验

实验一,检查%f需要读取几个字节

int a=0, b=0, c=5;

printf("%f,%d\n", a, b, c);
输出结果:

0,5

结论:%f读取8个字节,即两个整型大小

实验二,检查%lf需要读取几个字节

int a=0, b=0, c=5;

printf("%lf,%d\n", a, b, c);

输出结果:

0,5

结论:%lf也读取8个字节(也许和机器位宽有关,我是32位的机器)

实验三,检查printf读取float类型数据

float a=0.0f;

int b=5;

printf("%f,%d\n", a, b);
输出结果:

0.0,5

结论:float类型只占4个字节的数据,但前面实验一已经证明%f会读8个字节,即double类型的宽度,所以,编译器在将float类型参数入栈的时候,事先转换成了double类型。

实验四,再次证明实验三的结论

float a=0.0f;

int b=5;

printf("%d,%d,%d\n", a, b);
输出结果:

0,0,5

结论:a在入栈的时候,占了8个字节。

这样也难怪用%08X打印会出现乱七八糟的结果。

并且我在搜索过程中也遇到了很多人回帖说用printf时不要出现自己都不能预料结果的用法。

下表列出了printf和scanf对于各种格式说明符可以接受的参数类型。
格式
printf
scanf
%c
int
char *
%d, %i
int
int *
%o, %u, %x
unsigned int
unsigned int *

所以对于float变量来说,就不应该用%x来打印。

这里还要额外说下:printf使用了vsprintf使用va_arg来取值,vsprintf内部,%x格式就是*(unsigned
long*)&f

那么对于float类型的变量如何打印其内存情况呢?

应该用*(int*)&f ; *(int*)(&f) ; *((int*)&f)

printf("1.0 = 0x%08X, 1.0 = (int)%d\n", *(int*)&f, *(int*)&f);

结果为:1.0 = 0x3F800000, 1.0 = (int)1065353216
这个结果也能按照IEEE754浮点数来解释了。

但是其中不同的是%x虽然也是*(unsigned int*)&f,但是f在入栈的时候是8字节入栈的,要防止这个出现必须要让f在外面被强转。

反正一句话,不要乱用,按照规则用,否则结果难以预料。这次先写这些,下次继续吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐