zoukankan      html  css  js  c++  java
  • [转]计算机中的浮点数

    对浮点数的一些理解

    前言

    相比int等整型,float等浮点类型的表示和存储较为复杂,但它又是一个无法回避的话题,那么就有必要对浮点一探究竟了。在计算机中,一般用IEEE浮点近似表示任意一个实数,那么它实际上又是如何表示的呢?

    IEEE浮点表示

    IEEE浮点标准用

     

    的形式近似表示一个数。并且将浮点数的位表示划分为三个字段:

    • 符号(sign)s决定这个数是负数(s=1)还是正数(s=0)。可以用一个单独的符号s直接编码符号s。

    • 尾数(signficand)M是一个二进制小数,它的范围是1~2-ξ或者是0~1-ξ。
      n位小数字段frac= fn-1......f0编码尾数M。

    • 阶码(exponent)E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。k位的阶码字段exp = ek-1......e0编码阶码E。

    在单精度浮点格式(c语言的float)中,s,exp和frac字段分别为1位,8位和23位,而双精度浮点格式(c语言中的double)中,s,exp和frac字段分别为1位,11位和52位。


    一个浮点数的常见比特位表示如下:

    • 单精度

    s(31)exp(30~23)frac(22~0)
    • 双精度

    s(53)exp(62~52)frac(51~0)

    而根据exp的值,被编码的值可以分为三大类不同的情况。下面进行一一解释。

    情况1:规格化的值

    即最普遍的情况,当exp,即阶码域既不为全0,也不为全1的情况。在这种情况下,阶码字段解释为以偏置(biased)形式表示有符号整数,即E=exp-Bias,exp是无符号数(1~254)。Bias是一个等于(2k-1)-1的偏置值,对于单精度来说,k=8,Bias=127,因此E的范围是-126~+127。

    frac被描述为小数值,且0≤frac<1,其二进制表示为0.frac。尾数定义为 M=1+frac ,则M=1.frac。那么就有1≤M<2,由于总是能够调整阶码E,使得M在范围1≤M<2,所以不需要显示的表示它,这样还能获得一个额外的精度位。也就是说,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的frac部分,等到读取的时候,再把第一位的1加上去。

    情况2:非规格化的值

    当exp,即阶码域为全0时,所表示的数便为非规格化的值,该情况下的阶码值E=1-Bias(注:为从非格式化值转换到格式化值提供了一种方法),尾数M=frac。

    非规格化的数有两个作用。

    • 表示数值0。格式化数中,我们总使得M≥1,因此就无法表示0。而阶码全0时,且尾数也全0时,就可以表示0了。

    • 表示接近0.0的数。它所表示的值分布地接近于0.0,该属性成为逐渐溢出。

    情况3:特殊值

    有两种:

    • 阶码全为1,小数域全为0。它得到值为 +∞(s=0)或-∞(s=1),它在计算机中可以表示溢出的结果,例如两个非常大的数相乘。

    • 阶码全为1,小数域不全为0。它得到值为NaN(Note a Number)。它在计算机中可以表示非法的数,例如计算根号-1时的值。

    浮点数的范围和有效位

    对于浮点数,其能表示的数值范围和其有效位如下

    类型比特位数值范围有效位
    float 32 -3.410^38~+3.410^38 6~7位
    double 64 -1.710^-308~1.710^308 15~16位
    long double 128 -1.210^-4932~1.210^4932 18~19位

    可见同比特位数的整型(例如int)要比浮点数(例如float)能表示的数值范围要小很多,但是需要注意的,虽然浮点数能表示的范围大,但是它却不能精确表示在其范围内的所有实数,也就是说,它只能保证有效位的值是精确的,当表示的数值(小数部分)超过有效位时,所表示的数是无法保证精确的,甚至可以说是错误的。


    那么浮点数的数值范围和有效位是如何得到的呢?

     

    浮点数的数值范围计算

    有了前面了基础,我们就可以来计算浮点数的数值范围了。以单精度(float)为例,我们知道它的指数范围(即E)为-126~+127,而M的范围为1≤M<2,实际上,对于单精度,1≤M≤2-2^(-23)(注:23为frac字段所占的比特位)。那么我们就可以得到单精度的最大值为:

         

    同理,我们可以得到单精度的最小值为:

         (-1)

    我们仅仅以单精度为例,用同样的方法可以计算其他精度的浮点数取值范围,在此不再赘述。

    浮点数的有效位

    有效位也可以理解为我们常说的精度。浮点数的精度是由尾数的位数来决定的。

    对于单精度(float),它的尾数为23位,而2^23=8388608,共7位,也就是说最多能有7位有效数字,但至少能保证6位,因此其有效位为6~7位。当然我们可以通过下面的内容进一步理解。以下计算结果保留10位小数。

        

    观察a和b的结果可以发现,0.0000001和0.0000002之间的其他数是没有办法通过单精度浮点数来精确表示的,也就是说,只有到小数点后面7位的值才是精确的,同理,观察b和c的结果,0.0000002到0.0000004之间的其他数也是不能通过单精度浮点数精确表示的,更不幸地是,这之间的数,甚至只能精确到第6位。

    这也就有了单精度浮点数的有效位为6~7位的结论。根据相似的方法,我们同样可以得到双精度浮点数的有效位为15~16位的结论,这里不再赘述。

    浮点数在内存中的存储

    了解了这么多,我们来看一下一个小数究竟是如何在内存中存储的。

    以float f = 8.25f为例。其二进制表示为1000.1=1000.1*23,可见指数实际值为3,则根据E=exp-Bias,可知exp=E+Bias=3+127=130,根据M=1+frac,可知,frac=M-1=0.0001(二进制)而


        

    因此不难得到,8.25的在内存中的存储情况为:

    sexpfrac
    0 1000 0010 0001 0000 0000 0000 0000 000

    如果这个时候把这个值作为整型使用,是多少呢?没错,是1090781184。

    #include<stdio.h>
    int main(int argc,char *argv[])
    {
        float f=8.25f;
        int *i = (int*)&f;
        printf("%d
    ",*i);
        return 0;
    }

    再说几句

    关于浮点数,需要再说几句:

    • 在二进制,第一个有效数字必定是“1”,因此这个“1”并不会存储。

    • 浮点数不能精确表示其范围内的所有数。

    • 可精确表示的数不是均匀分布的,越靠近0越稠密。

    • 默认舍入方式为向偶舍入,也被称为最接近的值舍入。

    • 不遵守普遍的算数属性,比如结合律。

  • 相关阅读:
    RabbitMQ消息队列-高可用集群部署实战
    python+rabbitMQ实现生产者和消费者模式
    RabbitMQ Connection Channel 详解
    Linux中安装Erlang
    基于 CentOS 搭建 Python 的 Django 环境
    redis单机安装
    CentOS7安装RabbitMQ(单机)
    iptables 规则行号,删除及插入规则
    jQuery前端生成二维码
    MailKit使用IMAP读取邮件找不到附件Attachments为空的解决方法
  • 原文地址:https://www.cnblogs.com/rockyching2009/p/10410467.html
Copyright © 2011-2022 走看看