深入理解 BigDecimal


 

什么是 BigDecimal為什么用 BigDecimal 而不用 double加減乘除常用方法保留兩位小數四舍五入比較注意事項參考

什么是 BigDecimal

BigDecimal 可以表示一個任意大小且精度完全准確的浮點數。

為什么用 BigDecimal 而不用 double

Talk is cheap, Show me the Code.

例 1:

 1double d1 = 0.3;
2double d2 = 0.2;
3System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));
4
5float f1 = 0.3f;
6float f2 = 0.2f;
7System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));
8
9BigDecimal bd1 = new BigDecimal("0.3");
10BigDecimal bd2 = new BigDecimal("0.2");
11System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));

運行結果

1Double:     0,3 - 0,2 = 0.09999999999999998
2Float:     0,3 - 0,2 = 0.10000001
3BigDec:     0,3 - 0,2 = 0.1

從運行結果可以得出,當我們要做精確的小數操作運算時,就需要用到 BigDecimal。那下面做一下除法運算,看看結果:

例 2:

 1double d1 = 10;
2double d2 = 3;
3System.out.println("Double:\t 10 / 3 = " + (d1 / d2));
4
5float f1 = 10f;
6float f2 = 3f;
7System.out.println("Float:\t 10 / 3 = " + (f1 / f2));
8
9// Exception!
10BigDecimal bd3 = new BigDecimal("10");
11BigDecimal bd4 = new BigDecimal("3");
12System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));

運行結果

1Double:     10 / 3 = 3.3333333333333335
2Float:     10 / 3 = 3.3333333
3Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

當結果除不進,並且沒有設置進位的狀態值,那就會拋出異常。正確的操作如下:

1System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4,4,BigDecimal.ROUND_HALF_UP)));

運行結果

1Double:     10 / 3 = 3.3333333333333335
2Float:     10 / 3 = 3.3333333
3BigDec:     10 / 3 = 3.3333

總結:當我們在精度要求非常高的時候,需要進行精確的計算,比如:貨幣,那我們就需要采用 java.math.BigDecimal 類來進行精確計算。

加減乘除

方法 敘述
add(BigDecimal) BigDecimal對象中的值相加,然后返回這個對象
subtract(BigDecimal) BigDecimal對象中的值相減,然后返回這個對象
multiply(BigDecimal) BigDecimal對象中的值相乘,然后返回這個對象
divide(BigDecimal) BigDecimal對象中的值相除,然后返回這個對象
 1public class BigDecimalCalculation {
2    static BigDecimal a = new BigDecimal("0.02");
3    static BigDecimal b = new BigDecimal("0.03");
4
5    public static void main(String[] args) {
6        System.out.println("a + b = " + a.add(b));
7        System.out.println("a - b = " + a.subtract(b));
8        System.out.println("a * b = " + a.multiply(b));
9        System.out.println("a ÷ b = " + a.divide(b,2,BigDecimal.ROUND_HALF_UP));
10    }
11
12}

運行結果:

1a + b = 0.05
2a - b = -0.01
3a * b = 0.0006
4a ÷ b = 0.67

常用方法

保留兩位小數

 1public class keepTwoDecimal {
2    public static void main(String[] args) {
3        BigDecimal num= new BigDecimal(13.154215);
4
5        //方式一
6        DecimalFormat df1 = new DecimalFormat("0.00");
7        String str = df1.format(num);
8        System.out.println(str);  //13.15
9
10        //方式二
11        // #.00 表示兩位小數 #.0000四位小數
12        DecimalFormat df2 =new DecimalFormat("#.00");
13        String str2 =df2.format(num);
14        System.out.println(str2);  //13.15
15
16        //方式三
17        //%.2f %. 表示 小數點前任意位數   2 表示兩位小數 格式后的結果為f 表示浮點型
18        String result = String.format("%.2f", num);
19        System.out.println(result);  //13.15
20    }
21}

四舍五入

  • ROUND_UP

舍入遠離零的舍入模式。

在丟棄非零部分之前始終增加數字(始終對非零舍棄部分前面的數字加 1)

例如:2.36 -> 2.4

  • ROUND_DOWN

接近零的舍入模式。

在丟棄某部分之前始終不增加數字(從不對舍棄部分前面的數字加1,即截短)。

例如:2.36 -> 2.3

  • ROUND_CEILING

接近正無窮大的舍入模式。

如果 BigDecimal 為正,則舍入行為與 ROUND_UP 相同;

如果為負,則舍入行為與 ROUND_DOWN 相同。

相當於是 ROUND_UPROUND_DOWN 的合集

  • ROUND_FLOOR

接近負無窮大的舍入模式。

如果 BigDecimal 為正,則舍入行為與 ROUND_DOWN 相同;

如果為負,則舍入行為與 ROUND_UP 相同。

ROUND_CEILING 正好相反

  • ROUND_HALF_UP

四舍五入

例如:2.35 -> 2.4

  • ROUND_HALF_DOWN

五舍六入

例如:2.35 -> 2.3

  • ROUND_HALF_EVEN

如果舍棄部分左邊的數字為奇數,則舍入行為與 ROUND_HALF_UP 相同(四舍五入);

如果為偶數,則舍入行為與 ROUND_HALF_DOWN 相同(五舍六入)。

例如:1.15 -> 1.1,1.25 -> 1.2

  • ROUND_UNNECESSARY

斷言請求的操作具有精確的結果,因此不需要舍入。

如果對獲得精確結果的操作指定此舍入模式,則拋出 ArithmeticException。

我覺得剩下得了解下就行,而且我感覺剩下有的就是錯的,比如 ROUND_HALF_DOWNROUND_HALF_EVEN,看下面的結果你就知道我說的是為什么了。

 1public class BigDecimalScaleTest {
2    public static void main(String[] args) {
3        double num = 2.35;
4        BigDecimal b = new BigDecimal(num);
5        // setScale(1) 表示保留一位小數
6        System.out.println("ROUND_UP,結果:" + b.setScale(1, BigDecimal.ROUND_UP).doubleValue());
7        System.out.println("ROUND_DOWN,結果:" + b.setScale(1, BigDecimal.ROUND_DOWN).doubleValue());
8        System.out.println("ROUND_CEILING,結果:" + b.setScale(1, BigDecimal.ROUND_CEILING).doubleValue());
9        System.out.println("ROUND_FLOOR,結果:" + b.setScale(1, BigDecimal.ROUND_FLOOR).doubleValue());
10        System.out.println("ROUND_HALF_UP,結果:" + b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue());
11        System.out.println("ROUND_HALF_DOWN,結果:" + b.setScale(1, BigDecimal.ROUND_HALF_DOWN).doubleValue());
12        System.out.println("ROUND_HALF_EVEN,結果:" + b.setScale(1, BigDecimal.ROUND_HALF_EVEN).doubleValue());
13        System.out.println("ROUND_UNNECESSARY,結果:" + b.setScale(1, BigDecimal.ROUND_UNNECESSARY).doubleValue());
14    }
15}

運行結果

1ROUND_UP,結果:2.4
2ROUND_DOWN,結果:2.3
3ROUND_CEILING,結果:2.4
4ROUND_FLOOR,結果:2.3
5ROUND_HALF_UP,結果:2.4
6ROUND_HALF_DOWN,結果:2.4 (來給我解釋解釋這個,說好的五舍六入呢)
7ROUND_HALF_EVEN,結果:2.4 (還有這個)
8Disconnected from the target VM, address: '127.0.0.1:59637', transport: 'socket'
9Exception in thread "main" java.lang.ArithmeticException: Rounding necessary

小結:常用的就是 ROUND_HALF_UPROUND_UPROUND_DOWN,其它的當個笑話就行

比較

a.compareTo(b)

a > b 返回 1;a = b 返回 0;a < b 返回 -1

 1public class BigDecimalCompare {
2    public static void main(String[] args) {
3        BigDecimal a = new BigDecimal("0.02");
4        BigDecimal b = new BigDecimal("0.01");
5        BigDecimal a2 = new BigDecimal("0.02");
6
7        System.out.println(" a > b 返回結果:" + a.compareTo(b));
8        System.out.println(" a = a2 返回結果:" + a.compareTo(a2));
9        System.out.println(" b < a 返回結果:" + b.compareTo(a));
10    }
11}

運行結果

1 a > b 返回結果:1
2 a = a2 返回結果:0
3 b < a 返回結果:-1

注意事項

在上面的使用中,我們都用的 String 給 BigDecimal 進行 賦值,而沒有使用 double 類型賦值,具體的原因看下面的例子:

 1public class BigDecimalTest {
2    public static void main(String[] args) {
3        BigDecimal num1 = new BigDecimal(0.005);
4        BigDecimal num2 = new BigDecimal(1000000);
5        BigDecimal num3 = new BigDecimal(-1000000);
6        //盡量用字符串的形式初始化
7        BigDecimal num12 = new BigDecimal("0.005");
8        BigDecimal num22 = new BigDecimal("1000000");
9        BigDecimal num32 = new BigDecimal("-1000000");
10
11        //加法
12        BigDecimal result1 = num1.add(num2);
13        BigDecimal result12 = num12.add(num22);
14        //減法
15        BigDecimal result2 = num1.subtract(num2);
16        BigDecimal result22 = num12.subtract(num22);
17        //乘法
18        BigDecimal result3 = num1.multiply(num2);
19        BigDecimal result32 = num12.multiply(num22);
20        //絕對值
21        BigDecimal result4 = num3.abs();
22        BigDecimal result42 = num32.abs();
23        //除法
24        BigDecimal result5 = num2.divide(num1,20,BigDecimal.ROUND_HALF_UP);
25        BigDecimal result52 = num22.divide(num12,20,BigDecimal.ROUND_HALF_UP);
26
27        System.out.println("加法用value結果:"+result1);
28        System.out.println("加法用string結果:"+result12);
29
30        System.out.println("減法value結果:"+result2);
31        System.out.println("減法用string結果:"+result22);
32
33        System.out.println("乘法用value結果:"+result3);
34        System.out.println("乘法用string結果:"+result32);
35
36        System.out.println("絕對值用value結果:"+result4);
37        System.out.println("絕對值用string結果:"+result42);
38
39        System.out.println("除法用value結果:"+result5);
40        System.out.println("除法用string結果:"+result52);
41    }
42  }

運行結果:

 1加法用value結果:1000000.005000000000000000104083408558608425664715468883514404296875
2加法用string結果:1000000.005
3減法value結果:-999999.994999999999999999895916591441391574335284531116485595703125
4減法用string結果:-999999.995
5乘法用value結果:5000.000000000000104083408558608425664715468883514404296875000000
6乘法用string結果:5000.000
7絕對值用value結果:1000000
8絕對值用string結果:1000000
9除法用value結果:199999999.99999999583666365766
10除法用string結果:200000000.00000000000000000000

 

  • System.out.println() 中的數字默認是 double 類型的,double 類型的小數計算不准確
  • 使用 BigDecimal 的構造方法傳入 double 類型時,計算的結果也是不准確的!

所以我們在使用 BigDecimal 進行賦值的時候,最好使用傳入 String 的構造函數,可以確認精度。

參考

https://blog.csdn.net/haiyinshushe/article/details/82721234

https://www.jianshu.com/p/2947868d76eb

https://blog.csdn.net/ochangwen/article/details/51531866


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM