編寫高質量程式碼:改善Java程式的151個建議(第2章:基本型別___建議21~25)

阿赫瓦里發表於2016-09-12

                                           不積跬步,無以至千里;

                                      不積小流,無以成江海。

                                               ———荀子《勸學篇》

建議21:用偶判斷,不用奇判斷

   判斷一個數是奇數還是偶數是小學裡的基本知識,能夠被2整除的整數是偶數,不能被2整除的數是奇數,這規則簡單明瞭,還有什麼可考慮的?好,我們來看一個例子,程式碼如下: 

 1 import java.util.Scanner;
 2 
 3 public class Client21 {
 4     public static void main(String[] args) {
 5         // 接收鍵盤輸入引數
 6         Scanner input = new Scanner(System.in);
 7         System.out.println("輸入多個數字判斷奇偶:");
 8         while (input.hasNextInt()) {
 9             int i = input.nextInt();
10             String str = i + "-->" + (i % 2 == 1 ? "奇數" : "偶數");
11             System.out.println(str);
12 
13         }
14     }
15 }

輸入多個數字,然後判斷每個數字的奇偶性,不能被2整除的就是奇數,其它的都是偶數,完全是根據奇偶數的定義編寫的程式,我們開看看列印的結果:

  輸入多個數字判斷奇偶:1  2  0  -1 -2     1-->奇數    2-->偶數    0-->偶數     -1-->偶數       -2-->偶數

前三個還很靠譜,第四個引數-1怎麼可能是偶數呢,這Java也太差勁了吧。如此簡單的計算也會出錯!別忙著下結論,我們先來了解一下Java中的取餘(%識別符號)演算法,模擬程式碼如下:

// 模擬取餘計算,dividend被除數,divisor除數
    public static int remainder(int dividend, int divisor) {
        return dividend - dividend / divisor * divisor;
    }

看到這段程式,大家都會心的笑了,原來Java這麼處理取餘計算的呀,根據上面的模擬取餘可知,當輸入-1的時候,運算結果為-1,當然不等於1了,所以它就被判定為偶數了,也就是我們的判斷失誤了。問題明白了,修正也很簡單,改為判斷是否是偶數即可。程式碼如下:     i % 2 == 0 ? "偶數" : "奇數";

注意:對於基礎知識,我們應該"知其然,並知其所以然"。

建議22:用整數型別處理貨幣

 在日常生活中,最容易接觸到的小數就是貨幣,比如,你付給售貨員10元錢購買一個9.6元的零食,售貨員應該找你0.4元,也就是4毛錢才對,我們來看下面的程式:

public class Client22 {
    public static void main(String[] args) {
        System.out.println(10.00-9.60);
    }
}

我們的期望結果是0.4,也應該是這個數字,但是列印出來的卻是:0.40000000000000036,這是為什麼呢?

  這是因為在計算機中浮點數有可能(注意是有可能)是不準確的,它只能無限接近準確值,而不能完全精確。為什麼會如此呢?這是由浮點數的儲存規則所決定的,我們先來看看0.4這個十進位制小數如何轉換成二進位制小數,使用"乘2取整,順序排列"法(不懂,這就沒招了,這太基礎了),我們發現0.4不能使用二進位制準確的表示,在二進位制數世界裡它是一個無限迴圈的小數,也就是說,"展示"  都不能 "展示",更別說在記憶體中儲存了(浮點數的儲存包括三部分:符號位、指數位、尾數,具體不再介紹),可以這樣理解,在十進位制的世界裡沒有辦法唯一準確表示1/3,那麼在二進位制的世界裡當然也無法準確表示1/5(如果二進位制也有分數的話倒是可以表示),在二進位制的世界裡1/5是一個無限迴圈的小數。

  大家可能要說了,那我對結果取整不就對了嗎?程式碼如下

public class Client22 {
    public static void main(String[] args) {
        NumberFormat f = new DecimalFormat("#.##");
        System.out.println(f.format(10.00-9.60));
    }
}

列印出的結果是0.4,看似解決了。但是隱藏了一個很深的問題。我們來思考一下金融行業的計算方法,會計系統一般記錄小數點後的4為小數,但是在彙總、展現、報表中、則只記錄小數點後的2位小數,如果使用浮點數來計算貨幣,想想看,在大批量加減乘除後結果會有很大的差距(其中還涉及到四捨五入的問題)!會計系統要求的就是準確,但是因為計算機的緣故不準確了,那真是罪過,要解決此問題有兩種方法:

(1)、使用BigDecimal

    BigDecimal是專門為彌補浮點數無法精確計算的缺憾而設計的類,並且它本身也提供了加減乘除的常用數學演算法。特別是與資料庫Decimal型別的欄位對映時,BigDecimal是最優的解決方案。

(2)、使用整型

    把參與運算的值擴大100倍,並轉為整型,然後在展現時再縮小100倍,這樣處理的好處是計算簡單,準確,一般在非金融行業(如零售行業)應用較多。此方法還會用於某些零售POS機,他們輸入和輸出的全部是整數,那運算就更簡單了.

建議23:不要讓型別默默轉換

   我們做一個小學生的題目,光速每秒30萬公里,根據光線的旅行時間,計算月球和地球,太陽和地球之間的距離。程式碼如下: 

 1 public class Client23 {
 2     // 光速是30萬公里/秒,常量
 3     public static final int LIGHT_SPEED = 30 * 10000 * 1000;
 4 
 5     public static void main(String[] args) {
 6         System.out.println("題目1:月球照射到地球需要一秒,計算月亮和地球的距離。");
 7         long dis1 = LIGHT_SPEED * 1;
 8         System.out.println("月球與地球的距離是:" + dis1 + " 米 ");
 9         System.out.println("-------------------------------");
10         System.out.println("題目2:太陽光照射到地球需要8分鐘,計算太陽到地球的距離.");
11         // 可能要超出整數範圍,使用long型
12         long dis2 = LIGHT_SPEED * 60 * 8;
13         System.out.println("太陽與地球之間的距離是:" + dis2 + " 米");
14     }
15 }

  估計有人鄙視了,這種小學生的乘法有神麼可做的,不錯,就是一個乘法運算,我們執行之後的結果如下:

    題目1:月球照射到地球需要一秒,計算月亮和地球的距離。
       月球與地球的距離是:300000000 米
        -------------------------------
       題目2:太陽光照射到地球需要8分鐘,計算太陽到地球的距離.
       太陽與地球之間的距離是:-2028888064 米

  太陽和地球的距離竟然是負的,詭異。dis2不是已經考慮到int型別可能越界的問題,並使用了long型嗎,怎麼還會出現負值呢?

  那是因為Java是先運算然後進行型別轉換的,具體的說就是因為dis2的三個運算引數都是int型,三者相乘的結果雖然也是int型,但是已經超過了int的最大值,所以其值就是負值了(為什麼是負值,因為過界了就會重頭開始),再轉換為long型,結果還是負值。

  問題知道了,解決起來也很簡單,只要加個小小的L即可,程式碼如下:

  long dis2 = LIGHT_SPEED * 60L * 8;

  60L是一個長整型,乘出來的結果也是一個長整型的(此乃Java的基本轉換規則,向資料範圍大的方向轉換,也就是加寬型別),在還沒有超過int型別的範圍時就已經轉換為long型了,徹底解決了越界問題。在實際開發中,更通用的做法是主動宣告型別轉化(注意,不是強制型別轉換)程式碼如下:

  long dis2 = 1L * LIGHT_SPEED * 60L * 8

  既然期望的結果是long型,那就讓第一個參與的引數也是Long(1L)吧,也就說明"嗨"我已經是長整型了,你們都跟著我一塊轉為長整型吧。

注意:基本型別轉換時,使用主動宣告方式減少不必要的Bug.

建議24:邊界還是邊界

   某商家生產的電子產品非常暢銷,需要提前30天預訂才能搶到手,同時還規定了一個會員可擁有的最多產品數量,目的是為了防止囤積壓貨肆意加價。會員的預訂過程是這樣的:先登入官方網站,選擇產品型號,然後設定需要預訂的數量,提交,符合規則即提示下單成功,不符合規則提示下單失敗,後臺的處理模擬如下:

 1 import java.util.Scanner;
 2 
 3 public class Client24 {
 4     // 一個會員擁有產品的最多數量
 5     public final static int LIMIT = 2000;
 6 
 7     public static void main(String[] args) {
 8         // 會員當前用有的產品數量
 9         int cur = 1000;
10         Scanner input = new Scanner(System.in);
11         System.out.println("請輸入需要預定的數量:");
12         while (input.hasNextInt()) {
13             int order = input.nextInt();
14             if (order > 0 && order + cur <= LIMIT) {
15                 System.out.println("你已經成功預定:" + order + " 個產品");
16             } else {
17                 System.out.println("超過限額,預定失敗!");
18             }
19         }
20 
21     }
22 }

  這是一個簡單的訂單處理程式,其中cur代表的是會員當前擁有的產品數量,LIMIT是一個會員最多擁有的產品數量(現實中,這兩個引數當然是從資料庫中獲得的,不過這裡是一個模擬程式),如果當前預訂數量與擁有數量之和超過了最大數量,則預訂失敗,否則下單成功。業務邏輯很簡單,同時在web介面上對訂單數量做了嚴格的校驗,比如不能是負值、不能超過最大數量等,但是人算不如天算,執行不到兩小時資料庫中就出現了異常資料:某會員擁有的產品數量與預定數量之和遠遠大於限額。怎麼會這樣呢?程式邏輯上不可能有問題呀,這如何產生的呢?我們來模擬一下,第一次輸入:

  請輸入需要預定的數量:800    你已經成功預定800個產品

  這完全滿足條件,沒有任何問題,繼續輸入:

  請輸入需要預定的數量:2147483647   你已經成功預定2147483647個產品

  看到沒有,這個數字已經遠遠超過了2000的限額,但是竟然預定成功了,真實神奇!
  看著2147483647這個數字很眼熟?那就對了,這個數字就是int型別的最大值,沒錯,有人輸入了一個最大值,使校驗條件失敗了,Why?我們來看程式,order的值是2147483647那再加上1000就超出int的範圍了,其結果是-2147482649,那當然是小於正數2000了!一句歸其原因:數字越界使校驗條件失效。

  在單元測試中,有一項測試叫做邊界測試(也叫臨界測試),如果一個方法接收的是int型別的引數,那麼以下三個值是必須測試的:0、正最大、負最小,其中正最大、負最小是邊界值,如果這三個值都沒有問題,方法才是比較安全可靠的。我們的例子就是因為缺少邊界測試,致使生產系統產生了嚴重的偏差。

  也許你要疑惑了,Web介面已經做了嚴格的校驗,為什麼還能輸入2147483647  這麼大的數字呢?是否說明Web校驗不嚴格?錯了,不是這樣的,Web校驗都是在頁面上通過JavaScript實現的,只能限制普通使用者(這裡的普通使用者是指不懂html,不懂HTTP,不懂Java的簡單使用者),而對於高手,這些校驗基本上就是擺設,HTTP是明文傳輸的,將其攔截幾次,分析一下資料結構,然後寫一個模擬器,一切前端校驗就成了浮雲!想往後臺提交個什麼資料還不是信手拈來!

建議25:不要讓四捨五入虧了一方

   本建議還是來重溫一個小學數學問題:四捨五入。四捨五入是一種近似精確的計算方法,在Java5之前,我們一般是通過Math.round來獲得指定精度的整數或小數的,這種方法使用非常廣泛,程式碼如下:

public class Client25 {
    public static void main(String[] args) {
        System.out.println("10.5近似值: "+Math.round(10.5));
        System.out.println("-10.5近似值: "+Math.round(-10.5));
    }
}

輸出結果為:10.5近似值: 11       -10.5近似值: -10
  這是四捨五入的經典案例,也是初級面試官很樂意選擇的考題,絕對值相同的兩個數字,近似值為什麼就不同了呢?這是由Math.round採用的舍入規則決定的(採用的是正無窮方向舍入規則),我們知道四捨五入是有誤差的:其誤差值是舍入的一半。我們以舍入運用最頻繁的銀行利息計算為例來闡述此問題。

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

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

  四舍:捨棄的數值是: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 

  要在Java5以上的版本中使用銀行家的舍入法則非常簡單,直接使用RoundingMode類提供的Round模式即可,示例程式碼如下:  

 1 import java.math.BigDecimal;
 2 import java.math.RoundingMode;
 3 
 4 public class Client25 {
 5     public static void main(String[] args) {
 6         // 存款
 7         BigDecimal d = new BigDecimal(888888);
 8         // 月利率,乘3計算季利率
 9         BigDecimal r = new BigDecimal(0.001875*3);
10         //計算利息
11         BigDecimal i =d.multiply(r).setScale(2,RoundingMode.HALF_EVEN);
12         System.out.println("季利息是:"+i);
13         
14     }
15 }

在上面的例子中,我們使用了BigDecimal類,並且採用了setScale方法設定了精度,同時傳遞了一個RoundingMode.HALF_EVEN參數列示使用銀行家法則進行近似計算,BigDecimal和RoundingMode是一個絕配,想要採用什麼方式使用RoundingMode設定即可。目前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。
  5. HALF_UP:最近數字舍入(5舍),這就是我們經典的四捨五入。
  6. HALF_DOWN:最近數字舍入(5舍)。在四捨五入中,5是進位的,在HALF_DOWN中卻是捨棄不進位。
  7. HALF_EVEN:銀行家演算法

  在普通的專案中舍入模式不會有太多影響,可以直接使用Math.round方法,但在大量與貨幣數字互動的專案中,一定要選擇好近似的計算模式,儘量減少因演算法不同而造成的損失。

注意:根據不同的場景,慎重選擇不同的舍入模式,以提高專案的精準度,減少演算法損失。

附錄:此處說的這些常量全部來自java的RoundingMode類,故而貼出此類的原始碼供大家參考。

  1 package java.math;
  2 /**
  3  * Specifies a <i>rounding behavior</i> for numerical operations
  4  * capable of discarding precision. Each rounding mode indicates how
  5  * the least significant returned digit of a rounded result is to be
  6  * calculated.  If fewer digits are returned than the digits needed to
  7  * represent the exact numerical result, the discarded digits will be
  8  * referred to as the <i>discarded fraction</i> regardless the digits'
  9  * contribution to the value of the number.  In other words,
 10  * considered as a numerical value, the discarded fraction could have
 11  * an absolute value greater than one.
 12  *
 13  * <p>Each rounding mode description includes a table listing how
 14  * different two-digit decimal values would round to a one digit
 15  * decimal value under the rounding mode in question.  The result
 16  * column in the tables could be gotten by creating a
 17  * {@code BigDecimal} number with the specified value, forming a
 18  * {@link MathContext} object with the proper settings
 19  * ({@code precision} set to {@code 1}, and the
 20  * {@code roundingMode} set to the rounding mode in question), and
 21  * calling {@link BigDecimal#round round} on this number with the
 22  * proper {@code MathContext}.  A summary table showing the results
 23  * of these rounding operations for all rounding modes appears below.
 24  *
 25  *<p>
 26  *<table border>
 27  * <caption><b>Summary of Rounding Operations Under Different Rounding Modes</b></caption>
 28  * <tr><th></th><th colspan=8>Result of rounding input to one digit with the given
 29  *                           rounding mode</th>
 30  * <tr valign=top>
 31  * <th>Input Number</th>         <th>{@code UP}</th>
 32  *                                           <th>{@code DOWN}</th>
 33  *                                                        <th>{@code CEILING}</th>
 34  *                                                                       <th>{@code FLOOR}</th>
 35  *                                                                                    <th>{@code HALF_UP}</th>
 36  *                                                                                                   <th>{@code HALF_DOWN}</th>
 37  *                                                                                                                    <th>{@code HALF_EVEN}</th>
 38  *                                                                                                                                     <th>{@code UNNECESSARY}</th>
 39  *
 40  * <tr align=right><td>5.5</td>  <td>6</td>  <td>5</td>    <td>6</td>    <td>5</td>  <td>6</td>      <td>5</td>       <td>6</td>       <td>throw {@code ArithmeticException}</td>
 41  * <tr align=right><td>2.5</td>  <td>3</td>  <td>2</td>    <td>3</td>    <td>2</td>  <td>3</td>      <td>2</td>       <td>2</td>       <td>throw {@code ArithmeticException}</td>
 42  * <tr align=right><td>1.6</td>  <td>2</td>  <td>1</td>    <td>2</td>    <td>1</td>  <td>2</td>      <td>2</td>       <td>2</td>       <td>throw {@code ArithmeticException}</td>
 43  * <tr align=right><td>1.1</td>  <td>2</td>  <td>1</td>    <td>2</td>    <td>1</td>  <td>1</td>      <td>1</td>       <td>1</td>       <td>throw {@code ArithmeticException}</td>
 44  * <tr align=right><td>1.0</td>  <td>1</td>  <td>1</td>    <td>1</td>    <td>1</td>  <td>1</td>      <td>1</td>       <td>1</td>       <td>1</td>
 45  * <tr align=right><td>-1.0</td> <td>-1</td> <td>-1</td>   <td>-1</td>   <td>-1</td> <td>-1</td>     <td>-1</td>      <td>-1</td>      <td>-1</td>
 46  * <tr align=right><td>-1.1</td> <td>-2</td> <td>-1</td>   <td>-1</td>   <td>-2</td> <td>-1</td>     <td>-1</td>      <td>-1</td>      <td>throw {@code ArithmeticException}</td>
 47  * <tr align=right><td>-1.6</td> <td>-2</td> <td>-1</td>   <td>-1</td>   <td>-2</td> <td>-2</td>     <td>-2</td>      <td>-2</td>      <td>throw {@code ArithmeticException}</td>
 48  * <tr align=right><td>-2.5</td> <td>-3</td> <td>-2</td>   <td>-2</td>   <td>-3</td> <td>-3</td>     <td>-2</td>      <td>-2</td>      <td>throw {@code ArithmeticException}</td>
 49  * <tr align=right><td>-5.5</td> <td>-6</td> <td>-5</td>   <td>-5</td>   <td>-6</td> <td>-6</td>     <td>-5</td>      <td>-6</td>      <td>throw {@code ArithmeticException}</td>
 50  *</table>
 51  *
 52  *
 53  * <p>This {@code enum} is intended to replace the integer-based
 54  * enumeration of rounding mode constants in {@link BigDecimal}
 55  * ({@link BigDecimal#ROUND_UP}, {@link BigDecimal#ROUND_DOWN},
 56  * etc. ).
 57  *
 58  * @see     BigDecimal
 59  * @see     MathContext
 60  * @author  Josh Bloch
 61  * @author  Mike Cowlishaw
 62  * @author  Joseph D. Darcy
 63  * @since 1.5
 64  */
 65 public enum RoundingMode {
 66 
 67         /**
 68          * Rounding mode to round away from zero.  Always increments the
 69          * digit prior to a non-zero discarded fraction.  Note that this
 70          * rounding mode never decreases the magnitude of the calculated
 71          * value.
 72          *
 73          *<p>Example:
 74          *<table border>
 75          *<tr valign=top><th>Input Number</th>
 76          *    <th>Input rounded to one digit<br> with {@code UP} rounding
 77          *<tr align=right><td>5.5</td>  <td>6</td>
 78          *<tr align=right><td>2.5</td>  <td>3</td>
 79          *<tr align=right><td>1.6</td>  <td>2</td>
 80          *<tr align=right><td>1.1</td>  <td>2</td>
 81          *<tr align=right><td>1.0</td>  <td>1</td>
 82          *<tr align=right><td>-1.0</td> <td>-1</td>
 83          *<tr align=right><td>-1.1</td> <td>-2</td>
 84          *<tr align=right><td>-1.6</td> <td>-2</td>
 85          *<tr align=right><td>-2.5</td> <td>-3</td>
 86          *<tr align=right><td>-5.5</td> <td>-6</td>
 87          *</table>
 88          */
 89     UP(BigDecimal.ROUND_UP),
 90 
 91         /**
 92          * Rounding mode to round towards zero.  Never increments the digit
 93          * prior to a discarded fraction (i.e., truncates).  Note that this
 94          * rounding mode never increases the magnitude of the calculated value.
 95          *
 96          *<p>Example:
 97          *<table border>
 98          *<tr valign=top><th>Input Number</th>
 99          *    <th>Input rounded to one digit<br> with {@code DOWN} rounding
100          *<tr align=right><td>5.5</td>  <td>5</td>
101          *<tr align=right><td>2.5</td>  <td>2</td>
102          *<tr align=right><td>1.6</td>  <td>1</td>
103          *<tr align=right><td>1.1</td>  <td>1</td>
104          *<tr align=right><td>1.0</td>  <td>1</td>
105          *<tr align=right><td>-1.0</td> <td>-1</td>
106          *<tr align=right><td>-1.1</td> <td>-1</td>
107          *<tr align=right><td>-1.6</td> <td>-1</td>
108          *<tr align=right><td>-2.5</td> <td>-2</td>
109          *<tr align=right><td>-5.5</td> <td>-5</td>
110          *</table>
111          */
112     DOWN(BigDecimal.ROUND_DOWN),
113 
114         /**
115          * Rounding mode to round towards positive infinity.  If the
116          * result is positive, behaves as for {@code RoundingMode.UP};
117          * if negative, behaves as for {@code RoundingMode.DOWN}.  Note
118          * that this rounding mode never decreases the calculated value.
119          *
120          *<p>Example:
121          *<table border>
122          *<tr valign=top><th>Input Number</th>
123          *    <th>Input rounded to one digit<br> with {@code CEILING} rounding
124          *<tr align=right><td>5.5</td>  <td>6</td>
125          *<tr align=right><td>2.5</td>  <td>3</td>
126          *<tr align=right><td>1.6</td>  <td>2</td>
127          *<tr align=right><td>1.1</td>  <td>2</td>
128          *<tr align=right><td>1.0</td>  <td>1</td>
129          *<tr align=right><td>-1.0</td> <td>-1</td>
130          *<tr align=right><td>-1.1</td> <td>-1</td>
131          *<tr align=right><td>-1.6</td> <td>-1</td>
132          *<tr align=right><td>-2.5</td> <td>-2</td>
133          *<tr align=right><td>-5.5</td> <td>-5</td>
134          *</table>
135          */
136     CEILING(BigDecimal.ROUND_CEILING),
137 
138         /**
139          * Rounding mode to round towards negative infinity.  If the
140          * result is positive, behave as for {@code RoundingMode.DOWN};
141          * if negative, behave as for {@code RoundingMode.UP}.  Note that
142          * this rounding mode never increases the calculated value.
143          *
144          *<p>Example:
145          *<table border>
146          *<tr valign=top><th>Input Number</th>
147          *    <th>Input rounded to one digit<br> with {@code FLOOR} rounding
148          *<tr align=right><td>5.5</td>  <td>5</td>
149          *<tr align=right><td>2.5</td>  <td>2</td>
150          *<tr align=right><td>1.6</td>  <td>1</td>
151          *<tr align=right><td>1.1</td>  <td>1</td>
152          *<tr align=right><td>1.0</td>  <td>1</td>
153          *<tr align=right><td>-1.0</td> <td>-1</td>
154          *<tr align=right><td>-1.1</td> <td>-2</td>
155          *<tr align=right><td>-1.6</td> <td>-2</td>
156          *<tr align=right><td>-2.5</td> <td>-3</td>
157          *<tr align=right><td>-5.5</td> <td>-6</td>
158          *</table>
159          */
160     FLOOR(BigDecimal.ROUND_FLOOR),
161 
162         /**
163          * Rounding mode to round towards {@literal "nearest neighbor"}
164          * unless both neighbors are equidistant, in which case round up.
165          * Behaves as for {@code RoundingMode.UP} if the discarded
166          * fraction is &ge; 0.5; otherwise, behaves as for
167          * {@code RoundingMode.DOWN}.  Note that this is the rounding
168          * mode commonly taught at school.
169          *
170          *<p>Example:
171          *<table border>
172          *<tr valign=top><th>Input Number</th>
173          *    <th>Input rounded to one digit<br> with {@code HALF_UP} rounding
174          *<tr align=right><td>5.5</td>  <td>6</td>
175          *<tr align=right><td>2.5</td>  <td>3</td>
176          *<tr align=right><td>1.6</td>  <td>2</td>
177          *<tr align=right><td>1.1</td>  <td>1</td>
178          *<tr align=right><td>1.0</td>  <td>1</td>
179          *<tr align=right><td>-1.0</td> <td>-1</td>
180          *<tr align=right><td>-1.1</td> <td>-1</td>
181          *<tr align=right><td>-1.6</td> <td>-2</td>
182          *<tr align=right><td>-2.5</td> <td>-3</td>
183          *<tr align=right><td>-5.5</td> <td>-6</td>
184          *</table>
185          */
186     HALF_UP(BigDecimal.ROUND_HALF_UP),
187 
188         /**
189          * Rounding mode to round towards {@literal "nearest neighbor"}
190          * unless both neighbors are equidistant, in which case round
191          * down.  Behaves as for {@code RoundingMode.UP} if the discarded
192          * fraction is &gt; 0.5; otherwise, behaves as for
193          * {@code RoundingMode.DOWN}.
194          *
195          *<p>Example:
196          *<table border>
197          *<tr valign=top><th>Input Number</th>
198          *    <th>Input rounded to one digit<br> with {@code HALF_DOWN} rounding
199          *<tr align=right><td>5.5</td>  <td>5</td>
200          *<tr align=right><td>2.5</td>  <td>2</td>
201          *<tr align=right><td>1.6</td>  <td>2</td>
202          *<tr align=right><td>1.1</td>  <td>1</td>
203          *<tr align=right><td>1.0</td>  <td>1</td>
204          *<tr align=right><td>-1.0</td> <td>-1</td>
205          *<tr align=right><td>-1.1</td> <td>-1</td>
206          *<tr align=right><td>-1.6</td> <td>-2</td>
207          *<tr align=right><td>-2.5</td> <td>-2</td>
208          *<tr align=right><td>-5.5</td> <td>-5</td>
209          *</table>
210          */
211     HALF_DOWN(BigDecimal.ROUND_HALF_DOWN),
212 
213         /**
214          * Rounding mode to round towards the {@literal "nearest neighbor"}
215          * unless both neighbors are equidistant, in which case, round
216          * towards the even neighbor.  Behaves as for
217          * {@code RoundingMode.HALF_UP} if the digit to the left of the
218          * discarded fraction is odd; behaves as for
219          * {@code RoundingMode.HALF_DOWN} if it's even.  Note that this
220          * is the rounding mode that statistically minimizes cumulative
221          * error when applied repeatedly over a sequence of calculations.
222          * It is sometimes known as {@literal "Banker's rounding,"} and is
223          * chiefly used in the USA.  This rounding mode is analogous to
224          * the rounding policy used for {@code float} and {@code double}
225          * arithmetic in Java.
226          *
227          *<p>Example:
228          *<table border>
229          *<tr valign=top><th>Input Number</th>
230          *    <th>Input rounded to one digit<br> with {@code HALF_EVEN} rounding
231          *<tr align=right><td>5.5</td>  <td>6</td>
232          *<tr align=right><td>2.5</td>  <td>2</td>
233          *<tr align=right><td>1.6</td>  <td>2</td>
234          *<tr align=right><td>1.1</td>  <td>1</td>
235          *<tr align=right><td>1.0</td>  <td>1</td>
236          *<tr align=right><td>-1.0</td> <td>-1</td>
237          *<tr align=right><td>-1.1</td> <td>-1</td>
238          *<tr align=right><td>-1.6</td> <td>-2</td>
239          *<tr align=right><td>-2.5</td> <td>-2</td>
240          *<tr align=right><td>-5.5</td> <td>-6</td>
241          *</table>
242          */
243     HALF_EVEN(BigDecimal.ROUND_HALF_EVEN),
244 
245         /**
246          * Rounding mode to assert that the requested operation has an exact
247          * result, hence no rounding is necessary.  If this rounding mode is
248          * specified on an operation that yields an inexact result, an
249          * {@code ArithmeticException} is thrown.
250          *<p>Example:
251          *<table border>
252          *<tr valign=top><th>Input Number</th>
253          *    <th>Input rounded to one digit<br> with {@code UNNECESSARY} rounding
254          *<tr align=right><td>5.5</td>  <td>throw {@code ArithmeticException}</td>
255          *<tr align=right><td>2.5</td>  <td>throw {@code ArithmeticException}</td>
256          *<tr align=right><td>1.6</td>  <td>throw {@code ArithmeticException}</td>
257          *<tr align=right><td>1.1</td>  <td>throw {@code ArithmeticException}</td>
258          *<tr align=right><td>1.0</td>  <td>1</td>
259          *<tr align=right><td>-1.0</td> <td>-1</td>
260          *<tr align=right><td>-1.1</td> <td>throw {@code ArithmeticException}</td>
261          *<tr align=right><td>-1.6</td> <td>throw {@code ArithmeticException}</td>
262          *<tr align=right><td>-2.5</td> <td>throw {@code ArithmeticException}</td>
263          *<tr align=right><td>-5.5</td> <td>throw {@code ArithmeticException}</td>
264          *</table>
265          */
266     UNNECESSARY(BigDecimal.ROUND_UNNECESSARY);
267 
268     // Corresponding BigDecimal rounding constant
269     final int oldMode;
270 
271     /**
272      * Constructor
273      *
274      * @param oldMode The {@code BigDecimal} constant corresponding to
275      *        this mode
276      */
277     private RoundingMode(int oldMode) {
278         this.oldMode = oldMode;
279     }
280 
281     /**
282      * Returns the {@code RoundingMode} object corresponding to a
283      * legacy integer rounding mode constant in {@link BigDecimal}.
284      *
285      * @param  rm legacy integer rounding mode to convert
286      * @return {@code RoundingMode} corresponding to the given integer.
287      * @throws IllegalArgumentException integer is out of range
288      */
289     public static RoundingMode valueOf(int rm) {
290         switch(rm) {
291 
292         case BigDecimal.ROUND_UP:
293             return UP;
294 
295         case BigDecimal.ROUND_DOWN:
296             return DOWN;
297 
298         case BigDecimal.ROUND_CEILING:
299             return CEILING;
300 
301         case BigDecimal.ROUND_FLOOR:
302             return FLOOR;
303 
304         case BigDecimal.ROUND_HALF_UP:
305             return HALF_UP;
306 
307         case BigDecimal.ROUND_HALF_DOWN:
308             return HALF_DOWN;
309 
310         case BigDecimal.ROUND_HALF_EVEN:
311             return HALF_EVEN;
312 
313         case BigDecimal.ROUND_UNNECESSARY:
314             return UNNECESSARY;
315 
316         default:
317             throw new IllegalArgumentException("argument out of range");
318         }
319     }
320 }

 

相關文章