回覆列表
  • 1 # daazhu1

    浮點數有效數字與其表示的關係

    源自於以下問題:

    float只能保證6位有效數字,那麼下面的程式輸出應該是多少呢?

    int main()

    {

    float f = 0.1234567;

    cout << f << endl;

    cout << setprecision(7) << f << endl;

    return 0;

    }

    我覺得應該是:

    0.123457

    0.1234570

    但是在Dev C++ 4.9.9.2(g++3.4.2)下的執行結果是:

    0.123457

    0.1234567

    setprecision(7)僅改變了cout輸出的位數,為什麼連float的精度也變了呢??

    解答:

    0.1234567在記憶體中的二進位制模式如下0x3dfcd6de,ieee754標準

    0

    01111011

    11111001101011011011110

    1.11111001101011011011110*(2^(123-127))=0.000111111001101011011011110

    實際上記憶體中存的是這樣的一個二進位制小數0.000111111001101011011011110,化成十進位制是0.12345670163631439208984375 。可以看到前面7位都是精確的,所以有效數字是6位的說法,應該這樣理解:這並非是一個絕對的說法,也就是說這裡6位有效數字是指float可以保證前6位肯定有效(實際上是由尾數域的長度計算出來的),但這個的意思不是意味著第7位就一定就是無效的了。

    以前好像說過,並非所有的十進位制數都可以用二進位制來表示,有些有限的十進位制小數,表示成二進位制實際上會變成了無限的了。至於這個二進位制在不同的精度設定下,打印出是什麼樣子,就跟setprecision有關了,我就不無聊了

    首先之所以有以上開始的那個疑問,在於用十進位制的思維去考慮計算機的二進位制計算。也就是說所謂的保證6位有效數字,並不是說0.1234567變成ieee浮點數表示後,就成了0.123457了,而是指在向ieee浮點數格式轉換的時候,保證6位有效數字,並不是說轉換過程就是把十進位制表示的第7位四捨五入就好了。

    實際上編譯器處理的過程是這樣的,首先讀取程式中的0.1234567,然後將其轉換成ieee754的浮點數格式,轉換的過程可以看成一個十進位制向二進位制的表示過程,根據ieee標準,可以確定應該轉換到第幾位,然後保留這些位就可以了,剩下位的處理就是一個舍入問題了。同時要注意到以下兩點:

    1.並非所有的有限十進位制小數都可以轉換為有限的二進位制表示,能否轉成有限位的二進位制表示,取決於小數轉換成最簡分數時,其分母的質因子,如果質因子只有2則可以完成有限的二進位制位轉換。否則無法用一個有限的二進位制小數表示原來的十進位制小數。比如1/10就是無法用有限二進位制小數表示的。這就意味著很多十進位制看似位數簡單的小數,無論如何也是無法用有限二進位制表示的,也就意味著轉換成二進位制後,與原數必然存在誤差。

    2.實數是無限的,而ieee浮點數格式代表的二進位制模式則是有限的,這也同樣意味著一個二進位制模式可能需要表示多個實數,也就意味著誤差出現的必然。

    比如上述的0.1234567由編譯器轉換為ieee格式的時候,根據ieee浮點格式,需要轉換到從第一個非0位開始後的第24個位置,然後往後的那些位數就要舍掉了,如何舍掉呢?ieee同樣規定了幾種舍入方法,通常採用的是舍入到偶數。

    根據標準,無法精確儲存的值向最接近的可儲存的值進行舍入。再看上面的0.1234567,的表示0.000111111001101011011011110 =0.12345670163631439208984375,我們來看它是不是最接近的呢,實際上是的,首先上面的是比原數大,所以我們看如果把原來的表示減1,則變成了0.000111111001101011011011101=0.123456694185733795166015625,顯然這個不如上面那個接近,而且它小於了原數,也就說明了,其他的表示將比它們差的更多。

    可見這樣的轉換,並不是隨機的,而是一個確定的過程,當無法精確表示時,要保證一個最接近的表示,所以產生了上面看似很複雜的0.12345670163631439208984375 ,讓人感到很疑惑,但是的確它就是可以儲存的最接近原數0.1234567的那個。

    另外,setprecision,只是決定了輸出0.12345670163631439208984375的多少位?實際上這裡面有暗含了一個二進位制到十進位制的轉換。

    浮點數的舍入

    任何有效數上的運算結果,通常都存放在較長的暫存器中,當結果被放回浮點格式時,必須將多出來的位元丟棄。 有多種方法可以用來執行舍入作業,實際上IEEE標準列出4種不同的方法:

    舍入到最接近:會將結果舍入為最接近且可以表示的值。

    朝+∞方向舍入:會將結果朝正無限大的方向舍入。

    朝-∞方向舍入: 會將結果朝負無限大的方向舍入。

    朝0方向舍入: 會將結果朝0的方向舍入。

  • 中秋節和大豐收的關聯?
  • IG戰隊南風透露“Ning又把帶女友帶到了基地”,JKL離開IG是正確的,怎麼評價?