不積跬步,無以至千里;
不積小流,無以成江海。
———荀子《勸學篇》
建議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)的近似演算法,其規則如下:
- 捨去位的數值小於5時,直接捨去;
- 捨去位的數值大於等於6時,進位後捨去;
- 當捨去位的數值等於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支援以下七種舍入方式:
- ROUND_UP:原理零方向舍入。向遠離0的方向舍入,也就是說,向絕對值最大的方向舍入,只要捨棄位非0即進位。
- ROUND_DOWN:趨向0方向舍入。向0方向靠攏,也就是說,向絕對值最小的方向輸入,注意:所有的位都捨棄,不存在進位情況。
- ROUND_CEILING:向正無窮方向舍入。向正最大方向靠攏,如果是正數,舍入行為類似於ROUND_UP;如果為負數,則舍入行為類似於ROUND_DOWN.注意:Math.round方法使用的即為此模式。
- ROUND_FLOOR:向負無窮方向舍入。向負無窮方向靠攏,如果是正數,則舍入行為類似ROUND_DOWN,如果是負數,舍入行為類似以ROUND_UP。
- HALF_UP:最近數字舍入(5舍),這就是我們經典的四捨五入。
- HALF_DOWN:最近數字舍入(5舍)。在四捨五入中,5是進位的,在HALF_DOWN中卻是捨棄不進位。
- 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 ≥ 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 > 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 }