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

Java BigDecimal 精確運算完整指南:解決浮點數精度丟失問題

     編輯:Java 相關知識

Java float/double 採用 IEEE 754 二進位浮點數,無法精確表示許多十進位小數(如 0.1),導致商業計算出現誤差。BigDecimal 提供任意精度定點數運算,是金融、電商系統的標準選擇。

問題演示


System.out.println(0.06 + 0.01);  // 0.06999999999999999
System.out.println(1.0 - 0.42);   // 0.5800000000000001
System.out.println(4.015 * 100);  // 401.49999999999994
System.out.println(303.1 / 1000); // 0.30310000000000004

原因:二進位無法精確表示 0.1、0.01 等十進位小數,CPU 存儲為近似值。

BigDecimal 核心用法

1. 建構物件(避坑指南)


// ❌ 錯誤:double 建構子繼承二進位誤差
new BigDecimal(0.1); // 0.1000000000000000055511151231257827021181583404541015625

// ✅ 正確:字串建構子精確保留十進位字面值
new BigDecimal("0.1"); // 0.1

// ✅ 正確:valueOf 靜態工廠(內部用 Double.toString)
BigDecimal.valueOf(0.1); // 0.1

// ✅ 正確:從長整數 + 模數構建(高效能場景)
BigDecimal.valueOf(12345L, 3); // 12.345

2. 四則運算(不可變模式)


BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("2.3");

a.add(b);       // 12.8
a.subtract(b);  // 8.2
a.multiply(b);  // 24.15
a.divide(b);    // 拋 ArithmeticException:非終結小數
a.divide(b, 2, RoundingMode.HALF_UP); // 4.57(指定精度與捨入模式)

3. 捨入模式

4. 比較與轉換


a.compareTo(b);        // 1 (a>b), 0 (相等), -1 (a

通用工具類 Arith


public final class Arith {
    private Arith() {}

    private static final int DEF_SCALE = 10;
    private static final RoundingMode DEF_MODE = RoundingMode.HALF_UP;

    public static double add(double v1, double v2) {
        return BigDecimal.valueOf(v1).add(BigDecimal.valueOf(v2)).doubleValue();
    }
    public static double sub(double v1, double v2) {
        return BigDecimal.valueOf(v1).subtract(BigDecimal.valueOf(v2)).doubleValue();
    }
    public static double mul(double v1, double v2) {
        return BigDecimal.valueOf(v1).multiply(BigDecimal.valueOf(v2)).doubleValue();
    }
    public static double div(double v1, double v2, int scale) {
        if (scale < 0) throw new IllegalArgumentException("scale < 0");
        return BigDecimal.valueOf(v1)
                .divide(BigDecimal.valueOf(v2), scale, DEF_MODE)
                .doubleValue();
    }
    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_SCALE);
    }
    public static double round(double v, int scale) {
        return BigDecimal.valueOf(v).setScale(scale, DEF_MODE).doubleValue();
    }
}

最佳實踐清單

  1. 永遠用字串或 valueOf 建構,禁用 new BigDecimal(double)。
  2. 除法必須指定精度與捨入模式,否則非終結小數拋異常。
  3. 比較用 compareTo,不要用 equals(模數敏感)。
  4. 金額儲存用長整數(分/厘),資料庫 DECIMAL(19,4),避免浮點數完全。
  5. 高效能場景:考慮 Apache Commons Math 或手寫定點數運算(長整數 + 固定小數位)。

總結

BigDecimal 解決浮點數精度問題的核心:十進位定點數表示 + 顯式精度控制。商業系統務必統一使用,資料庫層面同步採用 DECIMAL 型別,端到端消除精度丟失風險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
模式行為範例 (保留 2 位)
HALF_UP四捨五入1.235 → 1.24
HALF_DOWN五捨六入1.235 → 1.23
HALF_EVEN銀行家算法(向偶數捨入)1.235 → 1.24, 1.225 → 1.22
UP遠離零方向1.231 → 1.24
DOWN趨向零方向(截斷)1.239 → 1.23