銀行家的舍入法則RoundingMode.HALF_EVEN

目前Java支持以下七種舍入方式:

  1. ROUND_UP:原理零方向舍入。向遠離0的方向舍入,也就是說,向絕對值最大的方向舍入,只要捨棄位非0即進位。

  2. ROUND_DOWN:趨向0方向舍入。向0方向靠攏,也就是說,向絕對值最小的方向輸入,注意:所有的位都捨棄,不存在進位情況。

  3. ROUND_CEILING:向正無窮方向舍入。向正最大方向靠攏,如果是正數,舍入行為類似於ROUND_UP;如果為負數,則舍入行為類似於ROUND_DOWN.注意:Math.round方法使用的即為此模式。

  4. ROUND_FLOOR:向負無窮方向舍入。向負無窮方向靠攏,如果是正數,則舍入行為類似ROUND_DOWN,如果是負數,舍入行為類似以ROUND_UP。

    Advertisements

  5. HALF_UP:最近數字舍入(5舍),這就是我們經典的四捨五入

  6. HALF_DOWN:最近數字舍入(5舍)。在四捨五入中,5是進位的,在HALF_DOWN中卻是捨棄不進位。

  7. HALF_EVEN:銀行家演算法

今天具體來了解下銀行家演算法

我們知道銀行的盈利渠道主要是利息差,從儲戶手裡收攏資金,然後房貸出去,期間的利息差額便是所獲得利潤,對一個銀行來說,對付給儲戶的利息計算非常頻繁,人民銀行規定每個季度末月的20日為銀行結息日,一年有4次的結息日。

場景介紹完畢,我們回頭來看看四捨五入,小於5的數字被捨去,大於5的數字進位后捨去,由於單位上的數字都是自然計算出來的,按照利率計算可知,被捨去的數字都分佈在0~9之間,下面以10筆存款利息計算作為模型,以銀行家的身份來思考這個演算法:

Advertisements

四舍:捨棄的數值是:0.000、0.001、0.002、0.003、0.004因為是捨棄的,對於銀行家來說就不需要付款給儲戶了,那每舍一個數字就會賺取相應的金額:0.000、0.001、0.002、0.003、0.004.

五入:進位的數值是:0.005、0.006、0.007、0.008、0.009,因為是進位,對銀行家來說,每進一位就會多付款給儲戶,也就是虧損了,那虧損部分就是其對應的10進位補數:0.005、.0004、0.003、0.002、0.001.

因為捨棄和進位的數字是均勻分佈在0~9之間,對於銀行家來說,沒10筆存款的利息因採用四捨五入而獲得的盈利是:

0.000 + 0.001 + 0.002 + 0.003 + 0.004 - 0.005 - 0.004 - 0.003 - 0.002 - 0.001 = - 0.005;

也就是說,每10筆利息計算中就損失0.005元,即每筆利息計算就損失0.0005元,這對一家有5千萬儲戶的銀行家來說(對國內銀行來說,5千萬是個小數字),每年僅僅因為四捨五入的誤差而損失的金額是:

銀行賬戶數量(5千萬)*4(一年計算四次利息)*0.0005(每筆利息損失的金額)

5000*10000*0.0005*4=100000.0;即,每年因為一個演算法誤差就損失了10萬元,事實上以上的假設條件都是非常保守的,實際情況可能損失的更多。那各位可能要說了,銀行還要放貸呀,放出去這筆計算誤差不就抵消了嗎?不會抵消,銀行的貸款數量是非常有限的其數量級根本無法和存款相比。

這個演算法誤差是由美國銀行家發現的(那可是私人銀行,錢是自己的,白白損失了可不行),並且對此提出了一個修正演算法,叫做銀行家舍入(Banker's Round)的近似演算法,其規則如下:

  1. 捨去位的數值小於5時,直接捨去;

  2. 捨去位的數值大於等於6時,進位后捨去;

  3. 當捨去位的數值等於5時,分兩種情況:5後面還有其它數字(非0),則進位后捨去;若5後面是0(即5是最後一個數字),則根據5前一位數的奇偶性來判斷是否需要進位,奇數進位,偶數捨去。

以上規則匯總成一句話:四舍六入五考慮,五后非零就進一,五後為零看奇偶,五前為偶應捨去,五前為奇要進一。我們舉例說明,取2位精度;

round(10.5551) = 10.56 round(10.555) = 10.56 round(10.545) = 10.54

public static void main(String[] args) {

// 存款

BigDecimal d = new BigDecimal(888888);

// 月利率,乘3計算季利率

BigDecimal r = new BigDecimal(0.001875*3);

//計算利息

BigDecimal i =d.multiply(r).setScale(2,RoundingMode.HALF_EVEN);

System.out.println("季利息是:"+i);

}

本文主要參考於:編寫高質量代碼:改善Java程序的151個建議

Advertisements

你可能會喜歡