zoukankan      html  css  js  c++  java
  • 数据的表示与运算-浮点数

    数据的表示与运算-浮点数

    前言:

    计算机中,数字分为定点数和浮点数。相对于浮点数,定点数比较好理解,原码补码反码移码。而浮点数十分繁杂。

    关于浮点数的繁杂,我觉得最好的解释就是,(William M. Kahan)因其在浮点数运算标准的制定上的突出贡献而获得图灵奖。(Kahan)也是浮点数(IEEE754)标准的主要设计师。

    初识浮点数:

    假如说我们现在想要表示光速这样一个数值,我们可以怎么做?

    • (1:)采用整数方式把他写出来,那么就是(300...00m/s)。这样数字十分的长,与计算机不好保存。
    • (2:)采用科学计数法,那么就是(3*10^{8}m/s),那么如果我现在想保存这个数字,那么我只需要记录三个信息,第一个是(3),第二个是(10),第三个是(8)

    两种方法比较:

    • 很明显第一种方法需要我们用更多的存储空间来保存它,而对于科学计数法,我们并不需要记录那么多的数却能表示同样的数值。

    • 对于计算机而言,只能认识(0/1)符号,这在硬件实现上更为方便简单。所以我们这时候可以把(10)这个底数给取消掉,计算机默认他是(2),这样我们就可以只用保存两个数字,来表示这样一个大的数字。

    • 对于表示电子的质量,太阳系的直径,这样非常极端的数字时,科学计数法的优势显得更为明显。它更方便。

    • 但我们也可以发现,如果说我要表示的数字不是(300...000),而是(29935...13)这样的数字,而我科学计数法采用(2.9*10^{8}),那么我势必丢掉一些精度。

    • 在我的理解看来,浮点数的表示是用精度来换取表示范围。

    • 所以这里我们似乎也能理解为什么在(c)语言中,(double)(float)能表示更高的精度,因为(double)位数更高,他能表示更多的小数。

    • 也能多少的理解浮点数为什么叫浮点数,因为随着我数字的指数不同,小数点的位置也在随之改变。

    浮点数的表示:

    通常,浮点数可以表示为:(N=r^E*M)

    其中(r)是阶码的底,通常为(2),且与尾数的基数相同。

    (E)是阶码,(M)是尾数。

    如下所示:

    • 阶符 阶码的数值部分 数符 尾数的数值部分
      (J_f) (J_1J_2,...,J_m) (S_f) (S_1S_2,...,S_n)

    阶码是整数,阶符和阶码共同表示浮点数的表示范围以及小数点的实际位置;

    数符表示正负,尾数的数值表示浮点数的精度。

    浮点数规格化:

    先看门见山讲一下什么叫规格化。

    规格化规定尾数的最高数位必须是一个有效值。

    通过以上的阅读,我们可以发现,要想让精度最大化,那么我们就需要让尾数部分尽可能的保存有效的数字。

    比如说对于这两个数(二进制)

    • (2^{10}*0.01)(2^{01}*0.1).

    这两个数是相等的,但是第二个数明显可以在尾数上少保存一位(0),所以这时候我们可以对浮点数进行规格化,让他能表示更高的精度。

    所谓规格化,是指通过一定的操作改变浮点数的尾数和阶码的大小,让浮点数(非0)的尾数在最高位保证是一个有效值。

    有如下两种方法:

    • 左规:当浮点数运算结果为非规格化时需要进行规格化处理,将尾数算术左移一位,并将阶码减一(二进制)。(左规可能需要进行多次)
    • 右规:当浮点数运算尾数出现溢出时,也就是双符号为出现了(01/10),需要将尾数算术右移一位并将阶码加一。右规只进行一次。

    那么规格化的浮点数的尾数的范围就是(frac{1}{2}leq |M|leq 1)

    分析:

    • 假设用原码来表示尾数:
      • 正数:
        • 数的最大值是(0.11...111),此时真值为(1-2^{-n})
        • 数的最小值时(0.10...000),此时真值为(frac{1}{2})
        • 绝对值的范围为([frac{1}{2},1-2^{-n}])
      • 负数:
        • 数的最大值是(1.10...00),此时真值为(-frac{1}{2})
        • 数的最小值是(1.11...11),此时真值为(-(1-2^{-n}))
        • 绝对值的范围是([frac{1}{2},1-2^{-n}])
    • 假设用补码来表示尾数:
      • 正数:
        • 正数的补码与原码相同,不做分析。
      • 负数:
        • 负数的最大值为(1.011...1),最小值为(1.00...0)
        • 绝对值得范围为([frac{1}{2}+2^{-n},1])
        • 这里需要注意。尾数的最大值不是(1.10...00)这样的形式,因为(1.10...000)不是一个规格化数。
        • 这里我查了一些资料,有两种说法我比较认可,第一个是对于(1.10000),我可以接着对他规格化到(1.00000);第二个是方便机器的设计,对于原码,我可以判断他的尾数最高位是不是(1)来判断他是否规格化,对于补码,我可以判断他的数符和尾数最高位是否相同来判断他是否规格化。

    IEEE754标准:

    按照(IEEE754)标准,浮点数表示格式如下:

    • 数符 阶码(用移码表示) 尾数(用原码表示,隐藏最高位(1)
      (m_s) (E) (M)

    为了最大幅度的增大浮点数表示精度,我们尾数最高位如果为(1)我们将其隐藏。举个例子,假如说尾数是(1011),那么我们存储(011)

    (float)(double)都是满足(IEEE754)标准的浮点数。

    阶码以移码形式存在。对于短浮点数(float),偏置值为(127),对于长浮点数(double),偏置值为(1023)

    那么可以这么求:我先将(E)的看成补码形式求出其值,然后减去(127/1023)就是他的移码代表的值。

    • ((-1)^s*1.M*2^{E-127})(短浮点数)。
    • ((-1)^s*1.M*2^{E-1023})(长浮点数)。

    浮点数的加减运算:

    浮点数运算需要将阶码运算和尾数运算分隔开。且分成以下几步:

    • 对阶
    • 尾数加减
    • 规格化
    • 舍入
    • 判断溢出:阶码溢出

    接下来一一分析。

    对阶:

    对阶的目的是让两个操作数阶码相等。原则是小阶向大阶看齐的方法。将阶码小的数尾数右移,阶码加一知道阶码相等。当然因为右移需要舍弃数据,所以精度会受影响。

    • 尾数加减:
      • 对阶后进行定点数加减运算。
    • 规格化:
      • 按照上文所述的规格化进行左规或者右规。
    • 舍入:
      • 在对阶和右规的过程中,可能会尾数低位丢失,引起误差,常见的舍入方法有:
        • (1:)(0)(1)入法:尾数右移的时候如果是(0),就舍去;如果是(1),就在尾数末尾(+1),这样有可能让尾数溢出,需要进行一次右规。(假设那个尾数全是1,加上就会一直进位进位到溢出)。
        • (2:)恒置(1)法:尾数丢掉后不管是(1)还是(0),补上(1),这样可能会使尾数变大或者变小。
    • 溢出判断:

      最后一步需要判断溢出。

      在浮点数规格化部分已经知道尾数双符号位出现(01,10),并不表示溢出,将此数右规即可。

      浮点数的溢出是由阶码决定的。双符号阶码出现(01/10),这时候就溢出了。

      • (10:)阶码小于最小阶码,按机器零处理。
      • (01:)阶码大于最大阶码,进入中断处理。
  • 相关阅读:
    Swagger注解及参数细节的正确书写。
    TCP连接为什么三次握手四次挥手
    OpenMeetings安装
    [LeetCode]Best Time to Buy and Sell Stock III
    [LeetCode]Best Time to Buy and Sell Stock
    [LeetCode]Best Time to Buy and Sell Stock II
    [LeetCode]Delete Node in a Linked List
    [LeetCode]Lowest Common Ancestor of a Binary Search Tree
    [LeetCode]Invert Binary Tree
    [LeetCode]Same Tree
  • 原文地址:https://www.cnblogs.com/zxytxdy/p/11909332.html
Copyright © 2011-2022 走看看