BigDecimal 是java小數操做的一個專有類,在電商、金融行業 存儲跟金額有關的字段java
java裏面明明已經有了,float,double這種精度的小數,爲何還須要BigDecimal呢?
這難道不是多餘嗎?ide
接下來看一個例子:函數
1 @Test 2 public void testDoubleSimple() { 3 double a = 3; 4 double b = 10; 5 double c = a / b; 6 System.out.println(c); 7 }
控制檯輸出:0.3this
在小數操做中,咱們一般但願能有多種自由的定義方式。spa
例如在不一樣的場景可能須要返回: 0.3, 0.4, 0.333 不一樣精度,在不一樣的精度進位時但願能自主控制.net
這個時候,就輪到BigDecimal出場了code
加減乘除
首先來一段最簡單的加減乘除blog
1 @Test 2 public void testDecimalSimple() { 3 BigDecimal a = new BigDecimal(5); 4 BigDecimal b = new BigDecimal(40); 5 BigDecimal add = a.add(b); 6 BigDecimal subtract = a.subtract(b); 7 BigDecimal multiply = a.multiply(b); 8 BigDecimal divide = a.divide(b); 9 System.out.println("add:" + add); 10 System.out.println("subtract:" + subtract); 11 System.out.println("multiply:" + multiply); 12 System.out.println("divide:" + divide); 13 }
控制檯輸出內容以下:ip
add:45; subtract:-35; multiply:200; divide:0.125;
在瞭解了BigDecimal基本內容後,在去深刻的去使用它的精度內存
精度控制
精度有7種模式,舉例以下
1 @Test 2 public void testRound() { 3 // 正無窮大方向取整 4 System.out.println("celling:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.CEILING))); 5 // 負無窮大方向取整 6 System.out.println("floor:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.FLOOR))); 7 //向 0 的方向取整 8 System.out.println("down a:" + new BigDecimal(0.121, new MathContext(2, RoundingMode.DOWN))); 9 System.out.println("down b:" + new BigDecimal(-0.129, new MathContext(2, RoundingMode.DOWN))); 10 // 正數向正無窮大取整,負數向負無窮大取整 11 System.out.println("up a:" + new BigDecimal(0.121, new MathContext(2, RoundingMode.UP))); 12 System.out.println("up b:" + new BigDecimal(-0.129, new MathContext(2, RoundingMode.UP))); 13 /** 14 * 5,6,7,8,9 向上取整 15 * 1,2,3,4 向下取整 16 * 17 * 經常使用的4舍5入 18 */ 19 System.out.println("half up:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.HALF_UP))); 20 /** 21 * 6,7,8,9 向上取整 22 * 1,2,3,4,5 向下取整 23 * 24 * 5 向下取整 25 */ 26 System.out.println("half down:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.HALF_DOWN))); 27 28 /** 29 * 小數位是5時,判斷整數部分是奇數就進位 30 * 1,2,3,4, 捨棄 31 * 6,7,8,9, 進位 32 */ 33 System.out.println("odd a:" + new BigDecimal(5.4, new MathContext(1, RoundingMode.HALF_EVEN))); 34 System.out.println("odd b:" + new BigDecimal(5.5, new MathContext(1, RoundingMode.HALF_EVEN))); 35 /** 36 * 小數位是5時,判斷整數部分是偶數就捨棄 37 * 1,2,3,4, 捨棄 38 * 6,7,8,9, 進位 39 */ 40 System.out.println("even a:" + new BigDecimal(6.5, new MathContext(1, RoundingMode.HALF_EVEN))); 41 System.out.println("even b:" + new BigDecimal(6.6, new MathContext(1, RoundingMode.HALF_EVEN))); 42 }
控制檯輸出內容以下
celling:0.13; floor:0.12; down a:0.12; down b:-0.12; up a:0.13; up b:-0.13; half up:0.13; half down:0.12; odd a:5; odd b:6; even a:6; even b:7;
在 RoundingMode.XXXXX 類型的源碼註釋上面,有更加詳細的例子,能夠看到是怎麼舍入的
我認爲在電商,金融領域中,用BigDecimal最重要的緣由有兩個:
1. 精度準確
2. 除法運算支持好
因此必定要對除法作深刻的瞭解,作項目的時候,才能不會對這些類型感到疑惑
1 @Test 2 public void testDecimalDivide() { 3 BigDecimal a = new BigDecimal(5.4); 4 BigDecimal b = new BigDecimal(3.1); 5 BigDecimal divide = a.divide(b); 6 System.out.println("divide:" + divide); 7 }
出現異常:
1 java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
明明剛剛還好好的,怎麼如今出了事?
那是由於 5.四、3.1都是double類型轉換的 BigDecimal。
實際上5.4在內存中多是 5.40000003321546546 的內容。致使BigDecimal內部精度計算的時候,發生錯誤
這個錯誤是由於沒有指定精度致使的,咱們只要指定告終果的精度,就能夠避免這個問題。
推薦作法
1 @Test 2 public void testDecimalStandDivide() { 3 BigDecimal a = new BigDecimal(5.4); 4 BigDecimal b = new BigDecimal(3.1); 5 // 保留幾位小數 6 int scale = 2; 7 // 重點:務必是3個參數 8 BigDecimal divide = a.divide(b,scale,RoundingMode.HALF_UP); 9 System.out.println("divide:" + divide); 10 11 }
控制檯輸出:divide:1.74
咱們額外傳入第二個參數:保留的小數,指定告終果的精度,就能夠避免出現這種問題。
因此咱們平常用BigDecimal作除法運算的時候,務必寫成推薦的形式。避免出現了異常,本身還莫名其妙
默認除法精度
在文章的開頭的除法,是用整數轉成BigDecimal, 保留的3爲小數。 那默認狀況下會精確到幾位呢?
在跟進到divide函數內部時,發現了構造MathContext的部份內容:
1 MathContext mc = new MathContext( (int)Math.min(this.precision() + 2 (long)Math.ceil(10.0*divisor.precision()/3.0), 3 Integer.MAX_VALUE), 4 RoundingMode.UNNECESSARY);
整數 12345 的precision 是5
整數 332 的precision 是 3
小數5.4 的precision多是 5.40000065464698656565454454555 的長度。 值不固定
根據MathContext的第一個參數的計算方式獲得默認除法精度:
1. 當被除數爲:0x1 最低精度5
2. 當被除數爲:0xFFFFFFFF 最高精度36
BigDecimal 精度描述:
模式 | 描述 |
CEILING | 正無窮大方向取整 |
FLOOR | 負無窮大方向取整 |
DOWN | 向 0 的方向取整 |
UP | 正數向正無窮大取整,負數向負無窮大取整 |
HALF_UP | 5,6,7,8,9 向上取整、 1,2,3,4 向下取整、 經常使用的4舍5入 |
HALF_DOWN | 6,7,8,9 向上取整 1,2,3,4,5 向下取整 |
HALF_EVEN | 小數位是5時,判斷整數部分是奇數就進位、 小數位是5時,判斷整數部分是偶數就捨棄、 1,2,3,4, 捨棄、 6,7,8,9, 進位 |
————————————————參考連接:https://blog.csdn.net/mz4138/article/details/82708815