<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
    數據加載中……

    浮點數比較

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

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

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

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

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

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

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

    使用相對誤差就很完善嗎?
    也不是, 在某些特殊情況下, 相對誤差也不能代表全部
    比如在判斷空間三點是否共線的時候,使用判斷點到另外兩個點形成的線段的距離的方法的時候
    只用相對誤差是不夠的,應為線段距離可能很段,也可能很長,點到線段的距離,以及線段的長度做綜合比較的時候,需要相對誤差和絕對誤差結合的方式才可以
    相對完整的比較算法應該如下:
    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;
    }
    這樣才相對完整

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

    浮點數的內存結構


    根據IEEE的標準,浮點數的定義如下

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

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

    小數部分,并不是一個浮點數的實際的小數
    實際的小數在這個小數前面還保留了一個1
    拿浮點數1.0來說
    符號位是0, 實際指數是0,對應這里的指數就是127了,也就是0x7f
    而小數部分就是1.0了, 1是暗含的不存儲,實際的小數部分就是0了
    因此組合起來的數據就是,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的浮點數格式說明
    對于0到1范圍內的浮點數是可以壓縮的

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

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

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


    在寫了上篇 浮點數的比較 以及 浮點數內存結構 兩篇文章后
    對于浮點數的比較有新的想法

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

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

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

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

    對于兩個負數進行比較的情況也是相同的
    只不過負數內存對應的整數加1,相應的找到的是更小的負數而已

    但是負數和整數之間現在還不能進行直接的比較,因為根據IEEE的內存結構
    正數和負數是不同的,對應的整數不能連續
    正的最小的數就是0了,對應的整數也是0x00000000
    負的最小的數就是-0,對應的整數則是0x 80000000
    不用奇怪-0
    在IEEE的表達當中是有兩個0的,一個是 +0 一個是-0

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

    通過對比我們可以發現,
    +0 和正的浮點數可以按照轉換成為整數的方式直接進行比較
    -0 和負的浮點數可以按照轉換成為整數的方式直接進行比較

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

    最后的比較算法就是:
    // 函數:   bool IsEqual(float f1, float f2, int absDelta)
    // 功能:把比較兩個浮點數是否近似相同
    // 輸入:f1, f2參與比較的兩個浮點數
    //               absDelta 兩個浮點數之間允許有多少個其他可以精確表達的浮點數存在,相當于相對誤差
    // 輸出:   true,兩個浮點數進行相等; false 兩個浮點數不等
    // 注意:僅僅適合IEEE 32位浮點數結構
    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 石頭@ 閱讀(4075) 評論(0)  編輯  收藏 所屬分類: java_base

    主站蜘蛛池模板: 污视频在线免费观看| 中国精品一级毛片免费播放| 国产a视频精品免费观看| 久久久久亚洲AV成人无码| 在线毛片片免费观看| 亚洲中文久久精品无码ww16| 久久www免费人成精品香蕉| 久久影视综合亚洲| 免费看无码特级毛片| 亚洲av日韩av天堂影片精品| 永久免费视频网站在线观看| 亚洲麻豆精品果冻传媒| 日本阿v免费费视频完整版| 亚洲国产精品日韩av不卡在线| 日本免费人成黄页网观看视频| 黄色网址大全免费| 亚洲最大AV网站在线观看| 一级毛片全部免费播放| 亚洲日本人成中文字幕| 国产女高清在线看免费观看| 久久免费国产精品| 久久久久亚洲精品成人网小说| 免费在线看v网址| 国产成人亚洲午夜电影| 亚洲日韩欧洲乱码AV夜夜摸| 免费国产黄网站在线观看可以下载 | 最好免费观看韩国+日本| 深夜特黄a级毛片免费播放| 久久精品国产精品亚洲精品| 日韩在线播放全免费| 精品在线视频免费| 亚洲AV无码专区亚洲AV伊甸园| 99爱在线精品免费观看| 成年大片免费高清在线看黄| 精品亚洲国产成AV人片传媒| 永久中文字幕免费视频网站| 成全视频高清免费观看电视剧| 亚洲影视自拍揄拍愉拍| 亚洲色爱图小说专区| 午夜视频在线观看免费完整版| 中国毛片免费观看|