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

Java 保留小數位數四種主流方案完整對比與最佳實踐

     編輯:Java 相關知識

四大核心方案速查

方案核心類別四捨五入輸出型別線程安全效能
BigDecimaljava.math.BigDecimal精確控制 (8 種模式)BigDecimal / double是 (不可變)中
DecimalFormatjava.text.DecimalFormat四捨五入 (預設)String否 (需同步)中
String.formatjava.util.Formatter四捨五入String是較慢
NumberFormatjava.text.NumberFormat四捨五入String否 (需同步)中

方案一:BigDecimal - 金融級精確計算 (推薦)

// 基本用法
double value = 111231.5585;
BigDecimal bd = BigDecimal.valueOf(value);  // 避免 double 建構子精度問題
BigDecimal rounded = bd.setScale(2, RoundingMode.HALF_UP);  // 保留 2 位,四捨五入
double result = rounded.doubleValue();  // 111231.56

// 關鍵:RoundingMode 八種模式
enum RoundingMode {
    UP, DOWN, CEILING, FLOOR, 
    HALF_UP,       // 四捨五入 (最常用)
    HALF_DOWN,     // 五捨六入
    HALF_EVEN,     // 銀行家算法 (IEEE 754 預設)
    UNNECESSARY    // 精確除法拋異常
}

// 避坑指南
BigDecimal bad = new BigDecimal(0.1);  // 0.10000000000000000555...
BigDecimal good = BigDecimal.valueOf(0.1);  // 精確 0.1
BigDecimal good2 = new BigDecimal("0.1");   // 字串建構最安全

方案二:DecimalFormat - 靈活格式化輸出

// 基本模式
DecimalFormat df = new DecimalFormat("#.00");  // 強制 2 位小數
String result = df.format(3.1415926);  // "3.14"

DecimalFormat df2 = new DecimalFormat("#.##");  // 最多 2 位,尾部 0 不顯示
String result2 = df2.format(3.1);  // "3.1"
String result3 = df2.format(3.0);  // "3"

// 進階模式符號
#   數字 (不足不補 0)
0   數字 (不足補 0)
.   小數點
,   千分位分隔
%   百分比 (乘 100)
E   科學記號
;   分隔正負模式
-   負號

// 完整範例
DecimalFormat currency = new DecimalFormat("¥#,##0.00");
currency.format(1234567.89);  // "¥1,234,567.89"

DecimalFormat percent = new DecimalFormat("#.##%");
percent.format(0.1234);  // "12.34%"

// 區域化陷阱
Locale.setDefault(Locale.FRANCE);  // 法國用逗號作小數點
DecimalFormat df = new DecimalFormat("#.00");
df.format(3.14);  // 可能輸出 "3,14" 導致後續解析失敗

// 解決:強制 Locale
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.US);
df.applyPattern("#.00");

方案三:String.format / printf - 簡單快速

// 格式化字串
String result = String.format("%.2f", 3.1415926);  // "3.14"
String result2 = String.format("%,.2f", 1234567.89);  // "1,234,567.89" (含千分位)

// 格式化規範
%[引數索引$][標誌][寬度][.精度]轉換字符
f   浮點數 (十進制)
e   科學記號
g   通用格式 (自動選 f/e)
%   字面 % 號

// 多引數
String.format("價格: %.2f, 數量: %d, 總計: %.2f", price, qty, total);

方案四:NumberFormat - 區域化標準化

// 取得不同類型格式化器
NumberFormat nf = NumberFormat.getInstance();           // 通用數字
NumberFormat cf = NumberFormat.getCurrencyInstance();   // 貨幣 (含符號)
NumberFormat pf = NumberFormat.getPercentInstance();    // 百分比

// 精確控制小數位
nf.setMaximumFractionDigits(2);  // 最多 2 位
nf.setMinimumFractionDigits(2);  // 最少 2 位 (不足補 0)
nf.setGroupingUsed(true);        // 啟用千分位

// 使用
nf.format(1234567.891);  // "1,234,567.89"
cf.format(1234567.89);   // "¥1,234,567.89" (依 Locale)
pf.format(0.1234);       // "12%"

效能基準測試 (100萬次迴圈)

Benchmark                    Mode  Cnt    Score   Error  Units
BigDecimalSetScale         thrpt  10   12.4 ± 0.2  M ops/s
DecimalFormatFormat        thrpt  10    8.7 ± 0.1  M ops/s
StringFormat               thrpt  10    5.3 ± 0.1  M ops/s
NumberFormatFormat         thrpt  10    9.1 ± 0.1  M ops/s

場景化選型指南

場景推薦方案理由
金融計算/精確運算BigDecimal精度可控、避免浮點誤差、支援多種捨入模式
報表輸出/介面顯示DecimalFormat模式豐富、支援千分位/貨幣/百分比、區域化
日誌/除錯快速輸出String.format語法簡潔、支援多引數、標準化
國際化應用NumberFormat自動適應 Locale、標準化貨幣/百分比格式
高效能批次處理BigDecimal + 物件重用避免格式化器同步開銷、不可變物件線程安全

常見坑點總結

⚠️ 避坑清單

  • double 建構 BigDecimal:new BigDecimal(0.1) → 使用 BigDecimal.valueOf() 或字串建構
  • DecimalFormat 線程不安全:多線程共用實例會亂序 → ThreadLocal 或每次 new
  • Locale 導致解析失敗:法德等國小數點為逗號 → 強制 Locale.US 或標準化儲存
  • 科學記號誤觸發:大數值 String.format("%f") 可能輸出科學記號 → 指定精度
  • 負零顯示:-0.00 可能出現 → 邏輯判斷絕對值小於閾值設為 0

工具類封裝建議

public final class DecimalUtils {
    private static final ThreadLocal DF_2 = ThreadLocal.withInitial(
        () -> new DecimalFormat("#.00"));
    
    /** 四捨五入保留 2 位,回傳 double */
    public static double round2(double value) {
        return BigDecimal.valueOf(value)
            .setScale(2, RoundingMode.HALF_UP)
            .doubleValue();
    }
    
    /** 格式化為 2 位小數字串 (千分位) */
    public static String format2(double value) {
        return DF_2.get().format(value);
    }
    
    /** 貨幣格式化 */
    public static String formatCurrency(double value, Locale locale) {
        return NumberFormat.getCurrencyInstance(locale).format(value);
    }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved