Java BigDecimal和double


BigDecimal類

對於不需要任何准確計算精度的數字可以直接使用float或double,但是如果需要精確計算的結果,則必須使用BigDecimal類,而且使用BigDecimal類也可以進行大數的操作。BigDecimal類的常用方法如表所示。

BigDecimal類的常用方法

序號

方    法

類型

描    述

1

public BigDecimal(double val)

構造

將double表示形式轉換

為BigDecimal

2

public BigDecimal(int val)

構造

將int表示形式轉換為

BigDecimal

3

public BigDecimal(String val)

構造

將字符串表示

形式轉換為BigDecimal

4

public BigDecimal add(BigDecimal augend)

普通

加法

5

public BigDecimal subtract(BigDecimal
subtrahend)

普通

減法

6

public BigDecimal multiply(BigDecimal
multiplicand)

普通

乘法

7

public BigDecimal divide(BigDecimal
divisor)

普通

除法

范例:進行四舍五入的四則運算

 1 package com.wy.test;
 2 
 3 import java.math.BigDecimal;
 4 
 5 public class BigDecimalTest {
 6     public static double add(double d1, double d2) { // 進行加法運算
 7         BigDecimal b1 = new BigDecimal(d1);
 8         BigDecimal b2 = new BigDecimal(d2);
 9         return b1.add(b2).doubleValue();
10     }
11 
12     public static double sub(double d1, double d2) { // 進行減法運算
13         BigDecimal b1 = new BigDecimal(d1);
14         BigDecimal b2 = new BigDecimal(d2);
15         return b1.subtract(b2).doubleValue();
16     }
17 
18     public static double mul(double d1, double d2) { // 進行乘法運算
19         BigDecimal b1 = new BigDecimal(d1);
20         BigDecimal b2 = new BigDecimal(d2);
21         return b1.multiply(b2).doubleValue();
22     }
23 
24     public static double div(double d1, double d2, int len) {// 進行除法運算
25         BigDecimal b1 = new BigDecimal(d1);
26         BigDecimal b2 = new BigDecimal(d2);
27         return b1.divide(b2, len, BigDecimal.ROUND_HALF_UP).doubleValue();
28     }
29 
30     public static double round(double d, int len) { // 進行四舍五入操作
31         BigDecimal b1 = new BigDecimal(d);
32         BigDecimal b2 = new BigDecimal(1);
33         // 任何一個數字除以1都是原數字
34         // ROUND_HALF_UP是BigDecimal的一個常量,表示進行四舍五入的操作
35         return b1.divide(b2, len, BigDecimal.ROUND_HALF_UP).doubleValue();
36     }
37 
38     public static void main(String[] args) {
39         System.out.println("加法運算:" + round(add(10.345, 3.333), 1));
40         // 加法運算:13.7
41         System.out.println("乘法運算:" + round(mul(10.345, 3.333), 3));
42         // 乘法運算:34.48
43         System.out.println("除法運算:" + div(10.345, 3.333, 3));
44         // 除法運算:3.104
45         System.out.println("減法運算:" + round(sub(10.345, 3.333), 3));
46         // 減法運算:7.012
47     }
48 }
 

BigDecimal是Java中用來表示任意精確浮點數運算的類,在BigDecimal中,使用unscaledValue × 10-scale來表示一個浮點數。其中,unscaledValue是一個BigInteger,scale是一個int。從這個表示方法來看,BigDecimal只能標識有限小數,不過可以表示的數據范圍遠遠大於double,在實際應用中基本足夠了。

下面提一下兩個精度問題:

問題一:BigDecimal的精度問題

1 System.out.println(new BigDecimal(0.1).toString()); // 0.1000000000000000055511151231257827021181583404541015625
2 System.out.println(new BigDecimal("0.1").toString()); // 0.1
3 System.out.println(new BigDecimal(
4 Double.toString(0.1000000000000000055511151231257827021181583404541015625)).toString());// 0.1
5 System.out.println(new BigDecimal(Double.toString(0.1)).toString()); // 0.1

分析一下上面代碼的問題(注釋的內容表示此語句的輸出)

第一行:事實上,由於二進制無法精確地表示十進制小數0.1,但是編譯器讀到字符串"0.1"之后,必須把它轉成8個字節的double值,因此,編譯器只能用一個最接近的值來代替0.1了,即0.1000000000000000055511151231257827021181583404541015625。因此,在運行時,傳給BigDecimal構造函數的真正的數值是0.1000000000000000055511151231257827021181583404541015625;
第二行:BigDecimal能夠正確地把字符串轉化成真正精確的浮點數;
第三行:問題在於Double.toString會使用一定的精度來四舍五入double,然后再輸出。會。Double.toString(0.1000000000000000055511151231257827021181583404541015625)輸出的事實上是"0.1",因此生成的BigDecimal表示的數也是0.1;
第四行:基於前面的分析,事實上這一行代碼等價於第三行。
 
結論:
1.如果你希望BigDecimal能夠精確地表示你希望的數值,那么一定要使用字符串來表示小數,並傳遞給BigDecimal的構造函數;
2.如果你使用Double.toString來把double轉化字符串,然后調用BigDecimal(String),這個也是不靠譜的,它不一定按你的想法工作;
3.如果你不是很在乎是否完全精確地表示,並且使用了BigDecimal(double),那么要注意double本身的特例,double的規范本身定義了幾個特殊的double值(Infinite,-Infinite,NaN),不要把這些值傳給BigDecimal,否則會拋出異常。

問題二:把double強制轉化成int,難道不是扔掉小數部分嗎?

 1 int x=(int)1023.99999999999999; // x=1024為什么? 

原因還是在於二進制無法精確地表示某些十進制小數,因此1023.99999999999999在編譯之后的double值變成了1024。

所以,把double強制轉化成int確實是扔掉小數部分,但是你寫在代碼中的值,並不一定是編譯器生成的真正的double值。
驗證代碼:
double d = 1023.99999999999999;
int x = (int) d;
System.out.println(new BigDecimal(d).toString()); // 1024
System.out.println(Long.toHexString(Double.doubleToRawLongBits(d))); // 4090000000000000
System.out.println(x); // 1024

前面提過BigDecimal可以精確地把double表示出來還記得吧。

我們也可以直接打印出d的二進制形式,根據IEEE 754的規定,我們可以算出0x4090000000000000=(1024)。


免責聲明!

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



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