首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java BigDecimal 精確運算完全指南:構造器選擇、核心方法與避坑實戰

     編輯:Java 綜合教學

為什麼需要 BigDecimal?

Java 基本類型 double/float 採用 IEEE 754 浮點數表示,無法精確表達許多十進制小數(如 0.1),導致財務、科學計算出現誤差:

System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(1.0 - 0.9); // 0.09999999999999998

java.math.BigDecimal 提供任意精度的有符號十進制數,完全解決精度丟失問題,是金融、電商、計量系統的標準選擇。

核心構造器:字符串構造器是唯一正解

構造器 說明 推薦度
new BigDecimal(String val) 精確解析十進制字符串,無精度損失 ⭐⭐⭐⭐⭐ 強烈推薦
new BigDecimal(double val) 直接使用 double 二進制近似值,會保留浮點誤差 ❌ 禁止使用
new BigDecimal(long val) / int 精確轉換整數 ⭐⭐⭐⭐ 適用整數場景
BigDecimal.valueOf(double val) 內部調用 Double.toString() 間接實現字符串構造,可接受 ⭐⭐⭐⭐ 兼容舊代碼

最佳實踐: 所有來自數據庫、前端、配置的數值統一以字符串形式傳遞給 BigDecimal 構造器。

核心運算方法:不可變模式與鏈式調用

BigDecimal 為不可變對象,所有運算方法返回新對象,原對象不變:

BigDecimal a = new BigDecimal("15124");
BigDecimal b = new BigDecimal("15124");

// 加減乘除
BigDecimal sum = a.add(b);           // 30248
BigDecimal diff = a.subtract(b);     // 0
BigDecimal prod = a.multiply(b);     // 228735376
BigDecimal quot = a.divide(b);       // 1 (精確整除)

// 除法非整除必須指定精度與舍入模式,否則拋 ArithmeticException
BigDecimal div = a.divide(new BigDecimal("3"), 10, RoundingMode.HALF_UP); // 5041.3333333333

// 比較與轉換
int cmp = a.compareTo(b);            // 0 相等
String str = a.toString();           // "15124"
double d = a.doubleValue();          // 15124.0 (可能精度損失)
long l = a.longValue();              // 15124
int i = a.intValue();                // 15124

除法精度控制:RoundingMode 詳細對照

模式 行為 示例 (保留 2 位)
UP遠離零方向捨入1.555 → 1.56 ; -1.555 → -1.56
DOWN趨向零方向捨入 (截斷)1.555 → 1.55 ; -1.555 → -1.55
CEILING向正無窮方向捨入1.555 → 1.56 ; -1.555 → -1.55
FLOOR向負無窮方向捨入1.555 → 1.55 ; -1.555 → -1.56
HALF_UP四捨五入 (最常用)1.555 → 1.56 ; 1.554 → 1.55
HALF_DOWN五捨六入1.555 → 1.55 ; 1.556 → 1.56
HALF_EVEN銀行家算法 (IEEE 754 默認)1.555 → 1.56 (前位偶) ; 1.545 → 1.54
UNNECESSARY要求精確,否則拋異常1.5 / 2 → 異常

實戰工具類封裝建議

public final class BigDecimalUtils {
    private static final int DEFAULT_SCALE = 10;
    private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_UP;

    private BigDecimalUtils() {}

    public static BigDecimal of(String val) {
        return val == null ? BigDecimal.ZERO : new BigDecimal(val.trim());
    }
    public static BigDecimal of(double val) {
        return BigDecimal.valueOf(val);
    }

    public static BigDecimal add(String a, String b) { return of(a).add(of(b)); }
    public static BigDecimal sub(String a, String b) { return of(a).subtract(of(b)); }
    public static BigDecimal mul(String a, String b) { return of(a).multiply(of(b)); }
    public static BigDecimal div(String a, String b) { 
        return of(a).divide(of(b), DEFAULT_SCALE, DEFAULT_ROUNDING); 
    }
    public static BigDecimal div(String a, String b, int scale) {
        return of(a).divide(of(b), scale, DEFAULT_ROUNDING);
    }

    public static String toPlainString(BigDecimal bd) {
        return bd == null ? "0" : bd.stripTrailingZeros().toPlainString(); // 去除科學計數法與尾部零
    }
}

常見陷阱總結

  1. 使用 double 構造器:new BigDecimal(0.1) 得到 0.10000000000000000555...。修正: new BigDecimal("0.1") 或 BigDecimal.valueOf(0.1)。
  2. 直接用 + - * / 運算符:編譯錯誤。修正:調用 add() subtract() multiply() divide()。
  3. 除法不指定精度:非整除拋 ArithmeticException: Non-terminating decimal expansion。修正:總是調用 divide(divisor, scale, roundingMode)。
  4. 比較用 equals:new BigDecimal("1.0").equals(new BigDecimal("1.00")) 為 false (scale 不同)。修正:使用 compareTo() == 0。
  5. 輸出科學計數法:toString() 大數值產生 1E+10。修正:用 toPlainString() 或 stripTrailingZeros().toPlainString()。

總結

BigDecimal 是 Java 精確計算的基石。核心原則:字符串構造、不可變運算、除法必配精度與舍入、比較用 compareTo、輸出用 toPlainString。封裝工具類統一風格,可將精度問題徹底隔離在業務代碼之外。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved