浮点数的存储结构和开发时候的注意事项

来源:互联网 发布:电力调度中心知乎 编辑:程序博客网 时间:2024/04/29 06:52
 
  • Overview

    在我们现在的计算系统中, 浮点数是以 IEEE 754  为标准的. 他的表示方式类似数学里提到的科学记数法.这种表示方式是用符号位 ( Sign )指数 ( Exponent )尾数 ( Mantissa )或者 也可以叫 Significand ( 有效位数 ) , 三者的组合来表达一个数字的.

    所以最好再开始看计算机中浮点数的表示方式前.首先简单回顾一下,小时候数学课中对科学记数法的描述.随后再了解编程中的浮点数.最后再看一下常有的注意事项.
    • 科学数法 ( Scientific notation )

    (大部分定义、推论.都出自 http://en.wikipedia.org )

    定义:
    一个数被写成一个1与10之间的实数(尾数)与一个10的幂的积,为了得到统一的表达方式,该尾数並不包括10.
    比如:
    782300 = 7.823 × 105
    0.00012 = 1.2 × 10−4
    10000 = 1×104

    推论 (科学记数的基本计算 乘、除法) :

    假设有两个以科学计数法表示的数字:




    则有:



    推论 (科学记数的基本计算 加、减法) :

    同样先假设有两个以科学计数法表示的数字:




    再做加、减法前先要把 X1 重新计算一下. 求出一个新的 c, 必须满足以下条件.



    接着就可以得到:



    举实际例子来说:

    • C/C++ 程序中的浮点数

    计算机程序中的浮点数的表示方式也类似上面提到的方式.只是在计算机中是以 2 为底数,而不是 10.

    需要注意:
    1. Intel CPU 是采用 little-endian ( The most significant byte is on the right end of a word. ) 所以在观察内存时是需要反过来读的.
    2. 有一些编译器 和 平台支持精度更高的 Double-Extended
    • C/C++ 编译器中的浮点数表示方式如下表所示.
      符号位 ( Sign ) 指数 ( Exponent ) 尾数 ( Mantissa ) 单精度浮点数 ( float ) 1 [31] 8 [30-23] 23 [22-00] 双精度浮点数 ( double ) 1 [63] 11 [62-52] 52 [51-00]
        • 符号
    符号位占用一位.在最高位. 0 表示正数. 1 表示负数.
        • 指数
    以 float 为例 指数位 占用 8 为, 他的表示范围是. -127 ~ +128. 但是 -127 和 +128 这 2 个数字是保留用作表示特殊状态的. ( -127 用来表示 这个是 0, 即 Zero 状态. +128 一般用来表示NaN 状态. NaN 是指 Not a Number. 不是一个实际的数字. )

    这 8 位在做计算是要注意范围是从 -127 ~ +128 的,所以在计算是要减去 127.
    比如有这 8位的指数2进制如下:
    10000000

    所以这个指数表示的是: 10000000b = 128 考虑到范围所以 128 - 127 = 1

    当精度为 double 时也是这样的,只是表示范围是 -1023 ~ +1024 .

        • 尾数
    尾数又叫有效位数 ( Significand ), 他和 科学记数中的尾数一样是用来表示这个数字的大小 和 精确度的.
    尾数的格式是 1.F. 其中 F 是对应的 小数位. 整数部分的 1 是在计算中省略的.

    • 举实际例子来说
    比如:
    有十进制 +5.5, 则有
    +5.5 = 1.375 × 22

    随后把 尾数换算成 2 进制.
    1.375 = 1 .011 B  ( 即,  1 × 21 + 1 × 2-2 + 1 × 2-3)

    由于 float 中尾数占用 23 位,所以要不齐那些无用的 0, 就得出 尾数为.
    1 .01100000000000000000000

    指数位是 2,由于 float 变换范围是 -127 ~ +128 占用 8 位. 所以指数位应该等于 2 + 127 = 10000001b

    所以就有 2 进制表示.
     符号位 ( Sign ) 指数 ( Exponent ) 尾数 ( Mantissa ) 01000000101100000000000000000000

    得出 +5.5 = 01000000101100000000000000000000B = 0x40B00000
    因为 little-endian 的关系,所以在内存中观察是反序的,即 [ 00 00 B0 40 ]
    • 开发时的注意事项

      • 比较操作
    不可将浮点变量用 “==” 或 “!=” 与任何数字比较.千万要留意,无论是float 还是double 类型的变量,都有精度限制.
    所以一定要避免将浮点变量用 “==” 或 “!=” 与数字比较,应该设法转化成 “>=” 或 “<=” 形式.

    假设浮点变量的名字为x,应当将
    if (x == 0.0) // 隐含错误的比较
    转化为
    if ((x>=-PRECISION) && (x<=PRECISION))
    其中PRECISION 是允许的误差(即精度).如果不是和 0 比较,则可用 与比较值之间的差值的绝对值与精度比较.比如: x 与 y 的比较可写成 fabs( x - y ) < PRECISION

      • 位运算
    正常情况下不要用 浮点数去做位运算.即使是你清楚浮点的存储结构.
    理由有二. 1. 不便于他人维护代码. 2. 可能有编译器不兼容问题.
    • Appendix

      • 浮点数运算的介绍http://steve.hollasch.net/cgindex/coding/ieeefloat.html
      • 浮点数转换运算的网页工具http://babbage.cs.qc.edu/IEEE-754/
      • 在 Attachments 里有 IEEE 754 的说明文件. (IEEE 754 是计算机中的浮点数标准) PS格式,可以用 GSView 浏览这个文件. (ieee754.ps )

    转载:http://sites.google.com/site/whirlpoolsite/float

  • 原创粉丝点击