zoukankan      html  css  js  c++  java
  • printf("%d",5.01)和printf("%f",5)的输出结果

    printf("%f ",5);
    printf("%d ",5.01);
    printf("%f ", (float)5);
    printf("%f ", 5.f);

    out:
    0.000000
    1889785610
    5.000000
    5.000000
    看到结果,会感觉非常奇怪。1处怎么会输出0呢?2又为何会显示这么大的一个数呢?
    解释:
    下面是转自网上的一篇博客的解释
    1,之所以没输出5,这是C语言设计的原因。
    2,之所以输出0,这是计算机体系结构的问题。
    具体来说:
    printf函数不会进行任何类型转换,它只是从内存中读出你所提供的元素的值(按照%d,%f等控制字符提示的格式)。C语言设计中,int类型一般是32bit或者16bit,而float一般是64bit,并且有可能使用科学计数保存。这点就和huhugo88所说一样,5在内存中为00000000,00000101。而且5一般都在静态区,程序的静态存储区默认是0,那么当用%f来读时,就会读64bit,也就是会读之前的很多位0,最后按照(有效数字)×(基数2)pow(指数)的方式来取数,自然结果是0
    之所以Vc中不允许这种情况,而有些编译器就允许这么输出就是编译器设置的问题。按理说,这样访问内存是属于越界访问,应该禁止。不过只是读,伤害性不大而已。 对于单精度浮点数(32bit),不少c语言编译系统以24位表示小数部分(包括1bit符号位),以8位表示指数部分。
    ==========================printf("%d ",5.01);
    为什么输出一个大数?在讲这个题目之前,预备知识,讲一下,printf函数,输入参数是读入缓冲区保存,再按照%?的格式从缓冲区中读出数据,并据此格式解释数据。
    有了这个知识之后,在讲程序员面试宝典上看到一个题:

    #include "stdio.h"
    int main(int argc, char* argv[])
    {
    printf("%d ",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(int argc, char* argv[])
    {
    double d = 5.01;
    int *p = (int *)(&d);
    int rst = 1889785610;
    printf("1).%x ",*p);
    printf("2).%x ",*(p+1));
    printf("3).%x ",rst);
    return 0;
    }
    输出为:
    1).0x70a3d70a
    2).0x40140a3d
    3).0x70a3d70a
    这也就证明了%d输出了5.01的低32低。5.01的double类型,在内存的的表示为0x40140a3d70a3d70a。
    事情看似也就完成了。
    我又想,如果输入是浮点类型的5.01f,又会发生什么呢?

    #include "stdio.h"
    int main(int argc, char* argv[])
    {
    float f = 5.01f;
    int *p = (int *)(&f);
    printf("1).0x%x ",*p);
    printf("2).0x%x ",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(int argc, char* argv[])
    {
    double f = 5.01;
    int *p = (int *)(&f);
    printf("1).0x%x ",*p);
    printf("2).0x%x ",*(p+1));
    printf("3).0x%x ",5.01f);
    return 0;
    }
    输出:
    1).0x70a3d70a
    2).0x40140a3d
    3).0x80000000
    但是我们发现结果并不一样,于是我又猜想,也是许printf将float转化为double的方式与默认的方式不一样
    5.01d的默认的表示为:0x40140a3d70a3d70a,在上面已经说明了

    #include "stdio.h"
    int main(int argc, char* argv[])
    {
    printf("0x%8x 0x%8x ",5.01f);
    return 0;
    }
    输出为:
    0x80000000
    0x40140a3d
    与是发现printf将5.01f->5.01d的表示是:0x40140a3d80000000
    接着就是看这两个值是否都是为5.01了:

    #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 ",*p1);
    printf("2).%f ",*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来取,只取前四个字节。

    From:http://blog.csdn.net/yahohi/article/details/7701434

  • 相关阅读:
    HDU 4278 Faulty Odometer 8进制转10进制
    hdu 4740 The Donkey of Gui Zhou bfs
    hdu 4739 Zhuge Liang's Mines 随机化
    hdu 4738 Caocao's Bridges tarjan
    Codeforces Gym 100187M M. Heaviside Function two pointer
    codeforces Gym 100187L L. Ministry of Truth 水题
    Codeforces Gym 100187K K. Perpetuum Mobile 构造
    codeforces Gym 100187J J. Deck Shuffling dfs
    codeforces Gym 100187H H. Mysterious Photos 水题
    windows服务名称不是单个单词的如何启动?
  • 原文地址:https://www.cnblogs.com/xiao0913/p/4161719.html
Copyright © 2011-2022 走看看