BigDecimal
?是 Java 中?java.math
?包提供的高精度十進制浮點數類,專為解決基本類型(float
/double
)的精度缺陷而設計,廣泛用于金融、科學計算等對精度要求極高的場景。以下從核心特性、使用方法、常見問題對比、注意事項等方面詳細介紹:
一、核心特性
任意精度
不像?double
(64 位,約 15-17 位有效數字)有固定精度限制,BigDecimal
?可通過?MathContext
?或?setScale
?自定義精度,理論上支持無限精度(僅受內存限制)。精確十進制表示
double
?是二進制浮點數,無法精確表示如?0.1
?這樣的十進制小數(會存儲為近似值),而?BigDecimal
?基于十進制算術,可精確存儲和計算小數。可控舍入模式
支持 8 種舍入規則(如四舍五入、向上取整、向下取整等),滿足不同場景的精度控制需求(如貨幣計算需精確到分)。超大數值范圍
突破?double
?的范圍限制(double
?最大約?1.8×103??
),可表示極大或極小的數(如?10^10000
?或?10^-10000
)。
二、基本使用方法
1. 初始化(關鍵!避免精度陷阱)
BigDecimal
?有多種構造方法,強烈推薦用字符串初始化,避免因?double
?本身的精度誤差導致問題:
import java.math.BigDecimal;// 正確方式:字符串構造(無精度損失)
BigDecimal num1 = new BigDecimal("0.1"); // 精確表示0.1
BigDecimal num2 = new BigDecimal("100.0000000001"); // 精確存儲多位小數// 錯誤方式:double構造(可能引入精度誤差)
BigDecimal bad1 = new BigDecimal(0.1); // 實際存儲的是0.1的二進制近似值(約0.1000000000000000055...)
BigDecimal bad2 = new BigDecimal(1.234567890123456789); // 超過double精度,會被截斷// 其他方式:整數或長整數構造
BigDecimal num3 = new BigDecimal(123); // 等價于new BigDecimal("123")
BigDecimal num4 = new BigDecimal(1234567890123456789L); // 大整數
2. 核心運算方法
BigDecimal
?不支持?+
、-
、*
、/
?等運算符,需通過以下方法進行運算,且運算結果需用新變量接收(原對象不變):
方法 | 功能 | 示例(num1=0.1,num2=0.2) |
---|---|---|
add(BigDecimal) | 加法 | num1.add(num2) ?→ 0.3 |
subtract(BigDecimal) | 減法 | num2.subtract(num1) ?→ 0.1 |
multiply(BigDecimal) | 乘法 | num1.multiply(num2) ?→ 0.02 |
divide(BigDecimal) | 除法(需指定舍入模式) | num1.divide(num2, 2, RoundingMode.HALF_UP) ?→ 0.50 |
pow(int) | 冪運算 | num1.pow(3) ?→ 0.001(0.13) |
3. 精度與舍入控制
(1)設置小數位數(setScale
)
BigDecimal num = new BigDecimal("3.1415926");// 保留2位小數,四舍五入(RoundingMode.HALF_UP)
BigDecimal scaled1 = num.setScale(2, RoundingMode.HALF_UP); // 3.14// 保留4位小數,向上取整(RoundingMode.UP)
BigDecimal scaled2 = num.setScale(4, RoundingMode.UP); // 3.1416(第5位是9,向上進1)// 保留1位小數,向下取整(RoundingMode.DOWN)
BigDecimal scaled3 = num.setScale(1, RoundingMode.DOWN); // 3.1(直接截斷)
(2)常用舍入模式(RoundingMode
)
模式 | 說明 | 示例(保留 1 位小數) |
---|---|---|
HALF_UP | 四舍五入(最常用) | 3.14 → 3.1;3.15 → 3.2 |
UP | 向上取整(遠離零) | 3.11 → 3.2;-3.11 → -3.2 |
DOWN | 向下取整(趨向零) | 3.19 → 3.1;-3.19 → -3.1 |
HALF_DOWN | 五舍六入 | 3.15 → 3.1;3.16 → 3.2 |
CEILING | 向正無窮取整 | 3.1 → 4.0;-3.1 → -3.0 |
FLOOR | 向負無窮取整 | 3.1 → 3.0;-3.1 → -4.0 |
4. 比較大小(compareTo
)
不能用?==
?或?equals
?比較(equals
?會嚴格比較精度,如?0.1
?與?0.10
?視為不等),需用?compareTo
:
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.10");
BigDecimal c = new BigDecimal("0.2");// compareTo 返回值:
// 0 → 相等;1 → a > b;-1 → a < b
System.out.println(a.compareTo(b)); // 0(數值相等,忽略精度差異)
System.out.println(a.compareTo(c)); // -1(0.1 < 0.2)
System.out.println(c.compareTo(a)); // 1(0.2 > 0.1)// 錯誤:equals 會比較精度(0.1 與 0.10 精度不同,返回false)
System.out.println(a.equals(b)); // false
5. 轉換為其他類型
BigDecimal num = new BigDecimal("123.456");// 轉換為基本類型(可能溢出,需謹慎)
double d = num.doubleValue(); // 123.456(可能損失精度)
long l = num.longValue(); // 123(截斷小數部分)// 轉換為字符串(推薦,無精度損失)
String s = num.toString(); // "123.456"
三、與?double
?的對比(解決哪些問題?)
場景 | double ?問題 | BigDecimal ?解決方案 |
---|---|---|
0.1 + 0.2 | 結果為?0.30000000000000004 (精度損失) | 結果為?0.3 (精確計算) |
貨幣計算(如分) | 無法精確表示分(如?0.01 ?存儲為近似值) | 可精確表示?0.01 ,避免金額誤差 |
超大 / 超小數值 | 超過范圍會溢出為?Infinity ?或?0 | 可表示任意大小數值(受內存限制) |
舍入規則控制 | 僅支持默認舍入,無法自定義 | 8 種舍入模式,靈活控制精度 |
四、注意事項(避坑指南)
初始化必須用字符串
用?double
?構造?BigDecimal
?會繼承?double
?的精度誤差(如?new BigDecimal(0.1)
?實際是?0.1000000000000000055...
),務必用?new BigDecimal("0.1")
。除法必須指定舍入模式
當除法無法整除時(如?1 ÷ 3
),不指定舍入模式會拋出?ArithmeticException
:// 錯誤:1 ÷ 3 無法整除,無舍入模式會拋異常 new BigDecimal("1").divide(new BigDecimal("3")); // 正確:指定舍入模式和精度 new BigDecimal("1").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP); // 0.33
避免頻繁創建對象
BigDecimal
?是不可變對象(運算后生成新對象),頻繁運算會產生大量臨時對象,影響性能。可重用對象或使用?MathContext
?控制精度。謹慎處理?
equals
?方法equals
?會比較數值和精度(如?0.1
?與?0.10
?視為不等),比較數值相等需用?compareTo
。性能權衡
BigDecimal
?運算效率遠低于?double
,非高精度場景(如普通科學計算)無需使用。
五、典型應用場景
- 金融 / 貨幣計算:如銀行轉賬、稅費計算(需精確到分,避免一分錢誤差)。
- 高精度科學計算:如物理、數學中的精確數值模擬(需保留多位有效數字)。
- 超大數值處理:如密碼學中的大整數運算、天文數據(超過?
double
?范圍)。