浮點數有效數字與其表示的關係
源自於以下問題:
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.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的方向舍入。
浮點數有效數字與其表示的關係
源自於以下問題:
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的方向舍入。