<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    海闊天空

    I'm on my way!
    隨筆 - 17, 文章 - 69, 評論 - 21, 引用 - 0
    數(shù)據(jù)加載中……

    浮點數(shù)比較

    在數(shù)學(xué)運算當中經(jīng)常會涉及到判斷兩個數(shù)是否相等的情況
    對于整數(shù)很好處理 A==B這樣的一個語句就可以解決全部的問題
    但是對于浮點數(shù)是不同的

    首先,浮點數(shù)在計算機當中的二進制表達方式就決定了大多數(shù)浮點數(shù)都是無法精確的表達的
    現(xiàn)在的計算機大部分都是數(shù)字計算機,不是模擬機,數(shù)字機的離散化的數(shù)據(jù)表示方法自然無法精確表達大部分的數(shù)據(jù)量的。

    其次計算機浮點數(shù)的精度在單精度float類型下,只有7位,在進行浮點運算的時候,這個精度往往會導(dǎo)致運算的結(jié)果和實際期望的結(jié)果之間有誤差

    因為前兩個原因,我們很難用 A==B來判定兩個浮點數(shù)是否相同

    很自然,我們可以想到 fabs(A-B) < epsilon 這樣的一種判別方法
    但是這種判別方法穩(wěn)妥嗎?
    它也不穩(wěn)妥。

    首先, epsilon是一個絕對的數(shù)據(jù),也就是誤差分析當中說說的絕對誤差
    使用一個固定的數(shù)值,對于float類型可以表達的整個數(shù)域來說是不可以的
    比如epsilon取值為0.0001,而a和b的數(shù)值大小也是0.0001附近的,那么顯然不合適
    另外對于a和b大小是10000這樣的數(shù)據(jù)的時候,它也不合適,因為10000和10001也可以認為是相等的呢
    適合它的情況只是a或者b在1或者0附近的時候

    既然絕對誤差不可以,那么自然的我們就會想到了相對誤差
    bool IsEqual(float a, float b, float relError ) {
           return ( fabs ( (a-b)/a ) < relError ) ? true : false;
    }
    這樣寫還不完善,因為是拿固定的第一個參數(shù)做比較的,那么在調(diào)用
    IsEqual(a, b, relError ) 和 IsEqual(b, a, relError ) 的時候,可能得到不同的結(jié)果
    同時如果第一個參數(shù)是0的話,就有可能是除0溢出
    這個可以改造
    把除數(shù)選取為a和b當中絕對數(shù)值較大的即可
    bool IsEqual(float a, float b, relError )
    {
          if (fabs(a)<fabs(b)) return ( fabs((a-b)/a) > relError ) ? true : false;
          return (fabs( (a-b)/b) > relError ) ? true : false;
    };

    使用相對誤差就很完善嗎?
    也不是, 在某些特殊情況下, 相對誤差也不能代表全部
    比如在判斷空間三點是否共線的時候,使用判斷點到另外兩個點形成的線段的距離的方法的時候
    只用相對誤差是不夠的,應(yīng)為線段距離可能很段,也可能很長,點到線段的距離,以及線段的長度做綜合比較的時候,需要相對誤差和絕對誤差結(jié)合的方式才可以
    相對完整的比較算法應(yīng)該如下:
    bool IsEqual(float a, float b, float absError, float relError )
    {
             if (a==b) return true;
            if (fabs(a-b)<absError ) return true;
            if (fabs(a>b) return (fabs((a-b)/a>relError ) ? true : false;
            return (fabs((a-b)/b>relError ) ? true : false;
    }
    這樣才相對完整

    這僅僅是浮點數(shù)之間最初級的比較方法
    高級的方法看 浮點數(shù)比較(二) 這個文章 —— 如何把兩個浮點數(shù)之間的比較轉(zhuǎn)化成為兩個整數(shù)之間的比較。
    -------------------------------------

    浮點數(shù)的內(nèi)存結(jié)構(gòu)


    根據(jù)IEEE的標準,浮點數(shù)的定義如下

    符號位 指數(shù)位 小數(shù)部分 指數(shù)偏移量
    單精度浮點數(shù) 1 位[31] 8位 [30-23] 23位 [22-00] 127
    雙精度浮點數(shù) 1 位[63] 11 位[62-52] 52 位[51-00] 1023

    我們以單精度浮點數(shù)來說明:
    符號位,表述浮點數(shù)的正或者負
    指數(shù)實際也有正負的,但是沒有單獨的符號位,而是采用了一個偏移來表示
    在計算機的世界里,進位都是二進制的,指數(shù)表示的也是2的N次冪
    這個數(shù)據(jù)格式當中的,指數(shù)是8位,可表達的范圍是0到255
    而對應(yīng)的實際的指數(shù)是-127到+128
    這里特殊說明,-127和+128這兩個數(shù)據(jù)在IEEE當中是保留的用作多種用途的
    -127表示的數(shù)字是0
    128和其他位數(shù)組合表示多種意義,最典型的就是NAN狀態(tài)

    小數(shù)部分,并不是一個浮點數(shù)的實際的小數(shù)
    實際的小數(shù)在這個小數(shù)前面還保留了一個1
    拿浮點數(shù)1.0來說
    符號位是0, 實際指數(shù)是0,對應(yīng)這里的指數(shù)就是127了,也就是0x7f
    而小數(shù)部分就是1.0了, 1是暗含的不存儲,實際的小數(shù)部分就是0了
    因此組合起來的數(shù)據(jù)就是,0x3f80000

    可以用一個類來表示:
    class FloatType
    {
    public:
          union {
             DWORD m_dwInt;
             float          m_fFloat;
           struct {  

            int    m_nFra: 23;

              int    m_nExp : 8;

              bool m_bSign : 1;

          };
    };

    -----------------------------

    參考IEEE的浮點數(shù)格式說明
    對于0到1范圍內(nèi)的浮點數(shù)是可以壓縮的

    顯然在0到1的范圍內(nèi),一個單精度的浮點數(shù),指數(shù)和符號位占據(jù)9個bit
    而這9個bit是可以不用的,把它去除,只保留小數(shù)部分的23bit就可以達到壓縮的目的
    可以把一個浮點數(shù)從32bit,4字節(jié)壓縮到23bit,3字節(jié)的范圍內(nèi)

    這也是在3dmax等一些工具軟件當中對浮點數(shù)進行壓縮存儲的方法。
    比如,在單位化的法向量當中,每個浮點數(shù)都是0,1范圍之間的數(shù)據(jù)
    正常情況下表示三維空間當中的單位化法向量就需要12個字節(jié)
    而經(jīng)過這個壓縮處理,只需要9個字節(jié)

    -----------------------------


    在寫了上篇 浮點數(shù)的比較 以及 浮點數(shù)內(nèi)存結(jié)構(gòu) 兩篇文章后
    對于浮點數(shù)的比較有新的想法

    我們先看正數(shù)的情況
    根據(jù)IEEE的內(nèi)存結(jié)構(gòu), 指數(shù)在高位,尾數(shù)在低位
    浮點數(shù)大的對應(yīng)的把其內(nèi)存結(jié)構(gòu)按照整數(shù)來理解進行比較的時候,情況也是成立的
    因此在這里如果把他們進行比較的話,作為整數(shù)運算效率會非常的高,比如
    float f1 = 1.23;
    float f2 = 1.24
    f1 > f2 成立
    (int&)f1 > (int&)f2 也是成立的

    而且,仔細研究IEEE的浮點結(jié)構(gòu),可以發(fā)現(xiàn)在《浮點數(shù)比較》當中提到的浮點數(shù)精度的問題——不是所有的浮點數(shù)都可以精確的表達
    可以精確表達的浮點數(shù)實際上是有限的,就是那些IEEE的各種情況的枚舉了 2^32個。不能表達的占據(jù)了大多數(shù)

    IEEE在32位的情況下,尾數(shù)是23位的(暗含了第一個位數(shù)是1)
    對于可以精確表達的浮點數(shù)來說,如果我們把這23位當作整數(shù)來理解, 它加1,就意味著可以找到比當前對應(yīng)浮點數(shù)大的最小的浮點數(shù)了
    反之,我們把兩個浮點數(shù),對應(yīng)的整數(shù)做差值運算,得到的整數(shù)表明的是兩個浮點數(shù)之間有多少個實際可以表達的浮點數(shù)(對應(yīng)的指數(shù)相同的情況下很好理解;指數(shù)不同的時候,也是同樣有效的)

    這樣,對于兩個正的浮點數(shù),他們的大小比較就可以用 (int&)f1 - (int&)f2 來進行比較了
    差值的結(jié)果實際上就應(yīng)該是相對誤差了
    這個相對誤差,不等同于普遍意義上的相對誤差
    它所表達的是,兩個浮點數(shù)之間可能還有多少個可以精確表達的浮點數(shù)
    這樣通過指定這個閾值來控制兩個浮點數(shù)的比較就更有效了
    對于兩個正的浮點數(shù)
    bool IsEqual(float f1, float f2, int absDelta)
    {
         if ( abs ( (int&)f1 - (int&)f2 ) < absDelta ) return true;
    }
    這里用abs而不是fabs這在asm上面的運算差距也是很大的了

    對于兩個負數(shù)進行比較的情況也是相同的
    只不過負數(shù)內(nèi)存對應(yīng)的整數(shù)加1,相應(yīng)的找到的是更小的負數(shù)而已

    但是負數(shù)和整數(shù)之間現(xiàn)在還不能進行直接的比較,因為根據(jù)IEEE的內(nèi)存結(jié)構(gòu)
    正數(shù)和負數(shù)是不同的,對應(yīng)的整數(shù)不能連續(xù)
    正的最小的數(shù)就是0了,對應(yīng)的整數(shù)也是0x00000000
    負的最小的數(shù)就是-0,對應(yīng)的整數(shù)則是0x 80000000
    不用奇怪-0
    在IEEE的表達當中是有兩個0的,一個是 +0 一個是-0

    有趣的是,按照 f1 == f2 的判斷 +0和-0是相等的

    通過對比我們可以發(fā)現(xiàn),
    +0 和正的浮點數(shù)可以按照轉(zhuǎn)換成為整數(shù)的方式直接進行比較
    -0 和負的浮點數(shù)可以按照轉(zhuǎn)換成為整數(shù)的方式直接進行比較

    如果我們能夠把他們連接起來,整個整數(shù)方式的直接比較就完備了
    對比一下負數(shù)的結(jié)構(gòu), 可以找到一個簡單的辦法了:
            把負數(shù)內(nèi)存對應(yīng)的整數(shù)減去 -0 ,他們就連續(xù)了
    而且更好的結(jié)果是,所有的負數(shù)經(jīng)過這次減法后,對應(yīng)的整數(shù)也都是負數(shù)了
    這樣整個整數(shù)比較就變得連續(xù)了,而且在整個浮點數(shù)范圍內(nèi)都是有效的了

    最后的比較算法就是:
    // 函數(shù):   bool IsEqual(float f1, float f2, int absDelta)
    // 功能:把比較兩個浮點數(shù)是否近似相同
    // 輸入:f1, f2參與比較的兩個浮點數(shù)
    //               absDelta 兩個浮點數(shù)之間允許有多少個其他可以精確表達的浮點數(shù)存在,相當于相對誤差
    // 輸出:   true,兩個浮點數(shù)進行相等; false 兩個浮點數(shù)不等
    // 注意:僅僅適合IEEE 32位浮點數(shù)結(jié)構(gòu)
    bool IsEqual(float f1, float f2, int absDelta)
    {
           int i1, i2;
           i1 = ( f1>0) ? ((int&)f1) : ( (int&) f1 - 0x80000000 );
           i2 = (f2>0) ? ((int&)f2) : ( (int&) f2 - 0x80000000 );
           return   ((abs(i1-i2))<absDelta) ? true : false;
    }

    posted on 2009-07-05 21:52 石頭@ 閱讀(4076) 評論(0)  編輯  收藏 所屬分類: java_base

    主站蜘蛛池模板: 亚洲电影唐人社一区二区| 亚洲精品无码不卡在线播放| 亚洲精品在线免费观看视频| 国产v亚洲v天堂a无| 免费h黄肉动漫在线观看| 久久福利青草精品资源站免费 | 亚洲AV午夜福利精品一区二区| 免费A级毛片无码A∨免费| 亚洲大码熟女在线观看| 亚洲欧洲自拍拍偷午夜色无码| 免费精品国偷自产在线在线| 一级黄色免费网站| 亚洲六月丁香六月婷婷色伊人| 国产成人aaa在线视频免费观看 | 午夜无码A级毛片免费视频| 亚洲国产AV无码一区二区三区 | 亚洲一区二区三区国产精品无码 | 亚洲精品中文字幕无乱码| 四虎在线播放免费永久视频 | 国产亚洲人成无码网在线观看| 日本阿v免费费视频完整版| 香蕉免费一级视频在线观看| 亚洲高清有码中文字| 国产AV无码专区亚洲AV毛网站| 麻豆精品国产免费观看| 久久精品免费视频观看| 日本特黄特色AAA大片免费| 亚洲国产精品成人综合色在线婷婷| 久久精品国产精品亚洲人人| 最新中文字幕免费视频| 无码专区AAAAAA免费视频| h视频在线免费观看| 亚洲风情亚Aⅴ在线发布| 亚洲喷奶水中文字幕电影| 亚洲区小说区图片区QVOD| 免费jjzz在线播放国产| 西西大胆无码视频免费| 三年片在线观看免费大全电影 | 亚洲精品国产第一综合99久久| 久久久久久亚洲精品成人| 亚洲精品夜夜夜妓女网|