計算機舍入問題

最近在學習 CSAPP(2e) 的時候才第一次意識到計算機中的舍入也不是一個簡單的內容。現做總結如下:

向整數舍入:


向整數舍入比較簡單理解,直接去掉小數,僅取整數部分,對於正負數均是如此,正數向下舍入,負數向上舍入,因此我們可以說成向零舍入(round-to-zero)。
右移與除法的舍入問題:
在C語言中有一個右移運算符>>,一般說來,向右移1位相當於除以2,就像下面這樣(k表示右移的位數,第二欄表示右移之後的值,第三欄表示實際運算結果):

k 右移k位 12340/(2^k)
0 12340 12340.0
1 6170 6170.0
4 771 771.25
8 48 48.203125

可以看到對於無符號的數來說,右移k位和除以2^k來說結果是一樣的。
但是如果問題擴展到有符號數,對於負數來說,結果可能會有點不一樣了

k 右移k位 -12340/(2^k)
0 -12340 -12340.0
1 -6170 -6170.0
4 -772 -771.25
8 -49 -48.203125

從上表中可以看到,當右移8位的時候,得到-49,這與向零舍入的結果-48並不吻合。
右移總是向下舍入,而對於向零舍入的負數來說是向上舍入,因此爲了保證結果吻合,需要進行一個偏置(biasing)。
對於負整數 x 和任意 y > 0 的y,x / y 向上舍入的結果和 ( x+y-1 ) / y 向下舍入的結果是相同,把 y = 2^k 帶入向下舍入的公式有, ( x +  2^k -1 ) / 2^k,換算成C語言的移位表達式也就是 
( x + (1 << k) - 1 ) >> k
這樣就可以保證負數通過這麼一個偏置,其“右移”的結果也是向上舍入。

總結起來,就是 x / (2^k) 等價的右移表達式爲:
( x < 0 ? x + (1<<k) - 1 : x) >> k

IEEE浮點數舍入:


浮點數舍入相比向整數舍入,就複雜一些,IEEE定義了四種不同的舍入方式,默認的爲向偶數舍入(round-to-even)。其他三個分別是向零舍入,向上舍入和向下舍入。

其他三個比較直觀,就不再說明,以十進制爲例說明“向偶數舍入”,平常的四捨五入中,以舍入到小數點後兩位爲例,如果第3位小數小於5,則捨去,大於等於5的則進1。向偶數舍入除了最中間的這個值(第3位小數爲5)以外,其他與平常的舍入相同,中間的這個值,則要保證在舍入的時候舍入位必須爲偶數。比如1.40, 1.60, 1.50, 2.50, -1.50向偶數舍入的結果分別爲1, 2, 2, 2, -2

至於爲什麼要想偶數舍入,這是CSAPP(2e)中的原文:

The problem with such a convention is that one can easily imagine scenarios 
in which rounding a set of data values would then introduce a statistical bias 
into the computation of an average of the values. The average of a set of numbers 
that we rounded by this means would be slightly higher than the average of 
the numbers themselves. Conversely, if we always rounded numbers halfway 
between downward, the average of a set of rounded numbers would be 
slightly lower than the average of the numbers themselves. Rounding toward 
even numbers avoids this statistical bias in most real-life situations. 
It will round upward about 50% of the time and round downward about 50% of the time.
也就是如果在一組數據中,向偶數舍入一般既有向上舍入也有向下舍入,可以一定程度上消除統計平均數時由於總是向上舍入時所帶來的統計偏差。

擴展成二進制,就是對形如 XX···X.YY···Y100···二進制模式位的數(最右邊的Y是要舍入的位置),根據舍入位是否爲偶數來選擇是向上舍入還是向下舍入。(至於爲什麼是100,我還沒想明白)
比如10.010, 10.011, 10.110, 11.001向偶數舍入(保留小數點後一位)的結果分別爲10.0, 10.1, 11.0, 11.0。

總結:


向整數舍入總是向零舍入,因此爲了保證負數右移 k 位的結果與除以 2^k 的結果是相同的,要做一個偏置。而浮點數舍入則爲向偶數舍入,中間值根據舍入位是否爲偶數來決定是向上還是向下舍入。

補充:對於V = 0.yyyyy··· 的無窮二進制位(其中 y 是一個 k 位的序列,例如 1/3 = 0.01010101···,y = 01,k = 2),滿足以下公式:
V = Y / (2^k - 1)
推倒:V 左移 k 爲爲 y.yyyy···,也即 Y + V = V * 2^k,變換一下就可以得到公式。
用這種方法可以快速計算出某些二進制位代表的十進制數,比如 0.001001001··· = 1 / (2^3 - 1) = 1 / 7,0.000111000111000111··· = 7 / (2^6 - 1) = 7 / 63 = 1 / 9
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章