从printf说开去(二)

     (接上回)

        我们在C/C++代码中使用:

            printf(“%f”, 10/3, 0×40080000 );

        看到运行结果了吗?为什么这行看起来不合乎所谓的语法的printf能输出3.000000呢?

        翻阅手册,回顾一下printf的格式化参数说明,你会发现%f的类型是double!
        我们知道在目前32位的机器上,sizeof(double) = 8字节 而sizeof(int) = 4字节,也就是说,机器在处理输出数据的时候,期望得到一个8字节的数据空间,而实际上只提供了一个4字节的数据空间,是不是这里出了问题?那么这个过程到底发生了些什么呢?

        10/3 这个数据由于是整数类型常数,他并不是在运行时进行计算的,而是在编译时,编译器把他翻译成了3。实质上,这段代码就与写printf(“%f”, 3)无异,他们完全等价。

        既然等价,为什么使用printf(“%f”, 3.0f);会输出正确的值?sizeof(3.0f) 不也等于4么?3.0f 和 整数3 是否等价?3.0和3.0f是否等价?

        似乎有点越扯越远,大家会不会有点糊涂了?要解释清楚这个过程确实需要一点口舌,不过别急,我们一个问题一个问题地说吧:


        问题一:printf(“%f”, 3.0f) 与 printf(“%f”, 3)的区别,3.0f和整数3在存储上有什么区别?3.0和3.0f在存储上有什么区别?

        其实,3.0f和整数3在内存中的表示是完全不同的,他们都占用4个字节空间,整数3在内存当中就是 0×00000003,而3.0f在内存中却为:0×40400000。

        并且,你会发现,3.0和3.0f在内存中的表示也不相同,3.0是一个双精度浮点数,他在内存中表示为:0×4008000000000000。你可以通过调试器查看这一点。
        那么浮点数是按什么规则表示的呢?IEEE标准从逻辑上用三元组{S,E,M}来表示浮点数N,其中S代表符号位,E代表指数位,M代表尾数。
        如果是单精度浮点数(float):N共32位,其中S占1位,E占8位,M占23位
        如果是双精度浮点数(double):N共64位,其中S占1位,E占11位,M占52位。

        (注:本文并不打算详细探讨浮点数的表示规则,有兴趣的朋友可自行参考IEEE754浮点数标准)
   
            N可以用以下公式算得:
            N = (-1)^S * m * 2^e

            当E的二进制位不全为0,也不全为1时,
            e = |E| – bias    (bias = 2^(k-1) – 1)
           单精度时k=8,bias=127 双精度时k=11,bias=1023

           其中m = |1.M|

           当E的二进制位全部为0时,此时:
            e = 1- bias
           m = |0.M|

 

           当E的二进制位全为1时,若M的二进制位全为0,则n表示无穷大,若S为1则表示负无穷,S为0则为正无穷。若M的二进制位不全为0时,表示NaN(Not a Number),代表着不合法或未初始化的值。

 

    例如:
    单精度浮点数3.0f,表示为2进制:
    S |            E         |                              M                                   |
    0  10000000  10000000000000000000000

    N = (-1)^0 * 1.5 * 2^1 = 3.0f

 

    双精度浮点数3.0,表示为2进制:
 符号位      指数位                                     尾数位
    S |                E              |                              M                                 |
    0  01000000000  1000 0000 0000 0000 … 0000

 

    看到了吗?他们的计算原理是一样的,但是位数不同,导致他们在内存里面的表示也不相同。


    3    在内存里面用16进制表示为:0×00000003
    3.0f 在内存里面用16进制表示为:0×40400000
    3.0  在内存里面用16进制表示为:0×4008000000000000

    我们现在知道了printf中%f是按double进行处理的,那么按双精度浮点规则,我们来看3是怎么被算成0的?


    3 用 64bit二进制表示:
    0000 0000 0000 0000 0000 0000 0000 … 0000 0011

    S |           E                    |                           M                                   |
    0  00000000000  0000 0000 0000 0000 … 0011


    这里的m已经是一个非常小,近乎于0的数字了,因此,在float保有的精度范围内,显示成为了0。


    有兴趣的同学可以算算,这个值大约等于:1.48乘以负的323次方。

 

    好,现在弄清楚了浮点数的表示,新的问题又来了,printf(“%f”, 10/3, 0×40080000 ); 能显示3.000000,这又是怎么工作的呢?

 

(未完待续)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值