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

那是在学习浮点数的一个精度问题的时候遇到的事情。

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

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在外面被强转。


反正一句话,不要乱用,按照规则用,否则结果难以预料。这次先写这些,下次继续吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值