在测试printf函数输出结果时,写了如下代码:

/**
 * printf:格式化输出函数
 *     printf函数不会按照格式控制而对数据类型进行转换,不管三七二十一,
 *     抓到二进制数据就按照格式控制符对数据进行解析。
 */
#include <stdio.h>
int main(void)
{
    //test_1
    float a = 10.9;
    printf("%d\n", a);
    //以十进制形式输出带符号整数(正数不输出符号)
    //输出 -1073741824
    //(printf 不会将浮点型变量 a 数据类型转换为整型,而是将按照浮点型格式存储的数据直接按照整型数据的格式进行解析打印)

    //test_2
    int b = 10;
    printf("%f\n", b);
    //以小数形式输出单、双精度浮点型
    //输出 0.000000

    //test_3(数据类型转换)
    int num_i = 10;
    float num_f = (float)num_i;
    printf("%f\n", num_f);
    //输出 10.000000
    //直接对变量进行类型强制转换时:
    //编译器先将内存中 num_i 的值当做整型数来处理,然后再按照浮点数的格式将其存储在内存中,最后 printf 以 %f (实数)格式打印出来

    //test_4(与 test_1、test_2、test_3 进行对比)
    int* fa = (int*)&a;
    float* fb = (float*)&b;
    printf("%d\n", *fa); //输出结果 1093559910
    printf("%d\n", *fb); //输出结果 0

    getchar();
    return 0;
}

本来以为test_4的结果会和test_1test_2结果相同,然而结果输出如下:

编译环境为 Dev_C++ 5.11,编译日志如下:

主要观察a*fa输出结果:在 test_4 中,取变量 a 的地址,然后将其地址强制转换成整型数地址,再取其地址中的内容 printf 按照%d格式输出。在这个过程中并没有改变地址内保存的内容,test_1 和 test_4 都是将其地址中保存的内容按照整型数据进行解析并打印输出,为什么结果会不一样呢??

主要参考了下面的几个帖子:

其中的重点是:printf 格式化输出,在传递参数时,若实参为 float 型数据,编译时会自动转换为 double 类型。我又写了一些代码用来测试,并总结这个问题,具体过程如下:

1.实型常量默认为 double 型

#include <stdio.h>
void main()
{
    //实型常量默认为 double 型,后面加 f/F 认为是 float 型
    printf("%d\n", sizeof(10.9));  //输出结果 8
    printf("%d\n", sizeof(10.9f)); //输出结果 4
}

2.打印如下数据

#include <stdio.h>
int main(void)
{
    float  a = 10.9;
    float  b = 10.9f;
    double c = 10.9;
    double d = 10.9f;
    printf("%d\n", a);             //输出结果 -1073741824
    printf("%d\n", b);             //输出结果 -1073741824
    printf("%d\n", c);             //输出结果 -858993459
    printf("%d\n", d);             //输出结果 -1073741824
    printf("%d\n", 10.9);          //输出结果 -858993459
    printf("%d\n", 10.9f);         //输出结果 -1073741824
    printf("%d\n", (double)10.9f); //输出结果 -1073741824

    //对比 test_4
    int* fa = (int*)&a;
    printf("%d\n", *fa);           //输出结果 1093559910

    return 0;
}

3.对比输出结果--总结

printf("%d\n", a);             //输出结果 -1073741824
printf("%d\n", b);             //输出结果 -1073741824
printf("%d\n", d);             //输出结果 -1073741824
printf("%d\n", 10.9f);         //输出结果 -1073741824
printf("%d\n", (double)10.9f); //输出结果 -1073741824

这里输出的都是-1073741824,只看前四行,对于 printf 传递的实参都是 float 型(由于 printf 格式化输出,在传递参数时,若实参为 float 型数据,编译时都会自动转换为 double 类型数据),所以实际输出结果均等价于第五行,先将 float 型的数据,自动转换为 double 类型,再将 8 字节的 double 类型数据按照%d格式解析输出。


printf("%d\n", c);             //输出结果 -858993459
printf("%d\n", 10.9);          //输出结果 -858993459

这里输出的都是-858993459,对于 printf 传递的实参都是 double 型,所以直接将 8 字节的 double 类型数据按照%d格式解析输出。那么这里的输出结果和上面的输出结果为什么不同??由于 float 类型和 double 类型直接转换会涉及到精度问题(看下面的代码),所以上面 float 类型的 10.9 转换为 double 类型的数据和 double 类型的 10.9 在计算机中是不同的,所以按照整型数来对数据(01011···)解析,输出的结果是不同的。

#include <stdio.h>
int main()
{
    printf("%.15f\n", 3.14f);
    printf("%.15f\n", (double)3.14f);
    printf("%.15f\n", 3.14);

    return 0;
}

其输出的结果如下:


int* fa = (int*)&a;
printf("%d\n", *fa);           //输出结果 1093559910

这里输出的结果是1093559910,和上面的两种结果都不相同,是因为将 float 类型的变量 a 的地址强制转换成整型数地址后,*fa将被看成是整型数(地址中保存的内容实际并没有改变),所以将*fa传递给 printf 函数,这里printf("%d\n", *fa);只是将变量 a 地址中 4 个字节的数据内容按照%d的格式进行解析并输出打印出来,因此,输出结果与上面两种结果都不同。

后记:换编译器

后来我换了VS 2015对程序进行编译,会直接给出警告,通过看VS 2015编译器给出的警告或许就可以直接发现问题了。对于上面 2.打印如下数据 中的程序,VS 2015的编译信息如下(结合行号看警告信息,注意第18行没有警告):


本来的程序只是为了测试 printf 函数的输出结果,用来验证“ printf 函数不会按照格式控制符而对数据类型进行强制转换。对于 printf,不管三七二十一,只是抓到二进制数据就按照格式控制符对数据进行解析。”但是细心观察,深入思考,就会发现新大陆:)


补充(两天后)

可以打印出变量 a、b、c、d 的地址,然后用调试工具查看数据在计算机内部的存储,可以对上述的 总结 加以验证,请参考 评论 ,具体代码如下:

#include <stdio.h>

int main(void)
{
    float  a = 10.9;
    float  b = 10.9f;
    double c = 10.9;
    double d = 10.9f;

    //打印变量的地址,查看数据在计算机内部的存储
    printf("%p\n", &a); // 0x412e6666          float 类型内部存储
    printf("%p\n", &b); // 0x412e6666          float 类型内部存储
    printf("%p\n", &c); // 0x4025cccccccccccd  double类型内部存储
    printf("%p\n", &d); // 0x4025ccccc0000000  由float类型转换为double类型,其内部存储

    printf("\n****************\n");

    //以下的printf输出语句是等价的
    printf("%d\n", a);                         //输出结果 -1073741824
    printf("%d\n", b);                         //输出结果 -1073741824
    printf("%d\n", d);                         //输出结果 -1073741824
    printf("%d\n", 10.9f);                     //输出结果 -1073741824
    printf("%d\n", (double)a);                 //输出结果 -1073741824
    printf("%d\n", (double)b);                 //输出结果 -1073741824
    printf("%d\n", (double)10.9f);             //输出结果 -1073741824
    printf("%d\n", 0x4025ccccc0000000);        //输出结果 -1073741824
    //printf对数据解析:截取低位四个字节 0xc0000000
    //补码:1100 0000 0000 0000 0000 0000 0000 0000
    //求原码,符号位不变,其余位取反加 1
    //原码:1100 0000 0000 0000 0000 0000 0000 0000
    //十进制表示:-1073741824

    printf("\n****************\n");

    //以下的printf输出语句是等价的
    printf("%d\n", c);                         //输出结果 -858993459
    printf("%d\n", 10.9);                      //输出结果 -858993459
    printf("%d\n", 0x4025cccccccccccd);        //输出结果 -858993459
    //printf对数据解析:截取低位四个字节 0xcccccccd
    //补码:1100 1100 1100 1100 1100 1100 1100 1101
    //求原码,符号位不变,其余位取反加 1
    //原码:1011 0011 0011 0011 0011 0011 0011 0011
    //十进制表示:-858993459

    printf("\n****************\n");

    //对比 test_4
    int* fa = (int*)&a;
    //以下的printf输出语句是等价的
    printf("%d\n", *fa);                       //输出结果 1093559910
    printf("%d\n", 0x412e6666);                //输出结果 1093559910
    //printf对数据解析:0x412e6666
    //补码:0100 0001 0010 1110 0110 0110 0110 0110
    //求原码,正数的原码、反码和补码相同
    //原码:0100 0001 0010 1110 0110 0110 0110 0110
    //十进制表示:1093559910

    getchar();
    return 0;
}

具体输出结果如下所示:

printf 输出浮点数的更多相关文章

  1. printf()输出

    printf()函数是式样化输出函数, 一般用于向准则输出设备按规定式样输出消息.正在编写步骤时经常会用到此函数.printf()函数的挪用式样为: printf("<式样化字符串&g ...

  2. [转载] c++ cout 格式化输出浮点数、整数及格方法

    C语言里可以用printf(),%f来实现浮点数的格式化输出,用cout呢...? 下面的方法是在网上找到的,如果各位有别的办法谢谢留下... iomanip.h是I/O流控制头文件,就像C里面的格式 ...

  3. [ZZ]c++ cout 格式化输出浮点数、整数及格式化方法

    C语言里可以用printf(),%f来实现浮点数的格式化输出,用cout呢...?下面的方法是在网上找到的,如果各位有别的办法谢谢留下... iomanip.h是I/O流控制头文件,就像C里面的格式化 ...

  4. //%f表示以十进制格式化输出浮点数 %.2f

    //%f表示以十进制格式化输出浮点数 String s1 ="评分: %.1f"; String s2 = String.format(s1, 8.0); System.out.p ...

  5. printf输出各种类型,cout控制输出各式

    ; char c = 'A'; int *p = &a; char *st = "ahj"; float x = 3.1415926; cout << & ...

  6. Win7超级终端查看单片机printf输出

    问题描述:     编写单片机C程序时,经常会用到printf输出信息进行查看,如何查看printf输出? 问题解决:     (1)编写单片机C程序     ucos是一个实时多任务操作系统,以上是 ...

  7. C++格式化输出浮点数

    主要内容 介绍C++中如何格式化输出浮点数. 控制浮点数输出格式需要包含iomanip头文件. 使用fixed来控制输出的浮点数的小数位是固定的.可参考http://en.cppreference.c ...

  8. 使用System.out.printf()输出日志重定向到文件后显示混乱问题

    写了一个小工具,使用System.out.printf()输出日志,以方便使用者查看,在终端显示没有问题,但重定向到文件就有问题了,会出现一些很奇怪的乱序现象. 上网查询资料,判断应该是跟重定向和Li ...

  9. 在进行多次scanf时,printf输出错误

    随便一处代码,经过改正后,输出正确的 ''' #include <stdio.h> int main(){    int T;    scanf("%d",&T ...

随机推荐

  1. windows 安装Bitcoin Core使用

    1.官网下载https://bitcoin.org/en/download 选择Windows  其他系统就选择对应的就好 2.双击安装完过后,进入bin目录,打开bitcoin-qt.exe运行,提 ...

  2. D - Lis on Circle Gym - 102441D (LIS + 线段树)

    There are nn people at the round gaming table. Each of them has a set of cards. Every card contains ...

  3. Git入门教程 Git教程入门

    一.下载与安装 在该页面 https://git-scm.com/download 选择操作系统自动下载. 默认安装就好了. 二,基本知识 三种状态:commited, modified, stage ...

  4. JavaSE--Java 的基本程序设计结构

    Java 对大小写敏感 Java 中定义类名的规则很宽松.名字必须以字母开头,后面可以跟字母和数字的任意组合.长度基本上没有限制.但是不能使用 Java 保留字作为类名. 标准的命名规范为:类名是以大 ...

  5. 添加头文件的报错failed to emit precompiled header 的解决办法

    在buildsetting中的以下两个路径中添加对应的设置,重现编译即可解决,stackoverflow地址:点击 Solution:1 I added $(inherited) non-recurs ...

  6. 伯特兰·亚瑟·威廉·罗素[註 1],第三代羅素伯爵(英语:Bertrand Arthur William Russell, 3rd Earl Russell,1872年5月18日-1970年2月2日),OM,FRS,英国哲学家、数学家和逻辑学家,致力于哲学的大众化、普及化。[2] 在數學哲學上採取弗雷格的邏輯主義立場,認為數學可以化約到邏輯,哲學可以像邏輯一樣形式系統化,主張逻辑原子論。[3]

    一年假. 1920年7月,罗素申請了一年假; 這被批准了.他花了一年時間在中國和日本講學.对中国学术界有相当影响. 罗素说:  对爱情的渴望,对知识的追求,对人类苦难不可遏制的同情,是支配我一生的单纯 ...

  7. Utf8BomRemover

    // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler ...

  8. Python语言学习前提:Pycharm的使用

    一.Pycharm的使用 1.点击Pycharm的图标 2.点击首页Create New Project > 在弹出的页面点击Pure Python 3.选择项目文件存放的位置,选择完成之后点击 ...

  9. pyCharm专业版最新2018激活码激活

    说明:本人亲测有用,对Window.Linux.Mac都稳定有效. 缺点:需要修改hosts文件 步骤: 由于管理权限问题,大部分电脑都不能直接修改hosts文件,所以我们可以先将hosts文件复制到 ...

  10. script和scriptreplay_超骚气的实时监控你的服务器

    今天看到一个超级叼的linux命令,可以完整记录屏幕上的命令与输出结果. 有人问这有什么叼的,不就是保存历史操作记录吗?我看看日志也能看出来. 不不不,我要说的“完整记录”包括第几秒执行什么命令,就像 ...