| 方法 | 適用場景 | 四捨五入 | 返回類型 | 線程安全 | 推薦指數 |
|---|---|---|---|---|---|
BigDecimal | 金融/精確計算 | 可控模式 | BigDecimal/double | 是 | ⭐⭐⭐⭐⭐ |
DecimalFormat | 顯示/序列化 | 預設半捨入 | String | 否(共享實例) | ⭐⭐⭐⭐ |
String.format | 日誌/快速輸出 | 半捨入 | String | 是 | ⭐⭐⭐ |
NumberFormat | 本地化顯示 | 半捨入 | String | 否 | ⭐⭐ |
double value = 111231.5585;
// 方式 1:setScale + ROUND_HALF_UP (經典四捨五入)
BigDecimal bd = BigDecimal.valueOf(value); // 推薦 valueOf 避免 double 構造器精度損失
double rounded = bd.setScale(2, RoundingMode.HALF_UP).doubleValue(); // 111231.56
// 方式 2:直接構造 (Java 9+ 推薦)
BigDecimal bd2 = BigDecimal.valueOf(value).setScale(2, RoundingMode.HALF_UP);
關鍵點:
BigDecimal.valueOf(double) 而非 new BigDecimal(double),前者利用 Double.toString 避免二進制浮點誤差RoundingMode:HALF_UP(四捨五入)、HALF_EVEN(銀行家算法)、DOWN(截斷)等BigDecimal 類型,勿轉回 doubledouble d = 3.1415926;
// 模式說明:# 可選數字,0 強制補位
DecimalFormat df1 = new DecimalFormat("#.00"); // 3.14
DecimalFormat df2 = new DecimalFormat("0.00"); // 3.14 (整數部分為0時顯示 0.00)
DecimalFormat df3 = new DecimalFormat("#,##0.00"); // 1,234.56 含千分位
String s1 = df1.format(d);
String s2 = df2.format(0.001); // "0.00"
注意:DecimalFormat 非線程安全,多線程環境下請使用 ThreadLocal 或每次新建實例。
double d = 3.1415926;
String s = String.format("%.2f", d); // "3.14"
System.out.printf("價格: %.2f 元%n", d); // 直接輸出
// 格式化語法:%[參數][標誌][寬度][.精度]轉換符
// %.2f → 保留 2 位小數的浮點數
// %,.2f → 含千分位分隔符
底層調用 Formatter,線程安全,性能一般,適合日誌、報表輸出。
double x = 23.5455;
NumberFormat nf = NumberFormat.getNumberInstance(Locale.CHINA); // 或 getInstance()
nf.setMaximumFractionDigits(2); // 最大小數位
nf.setMinimumFractionDigits(2); // 最小小數位(不足補0)
nf.setGroupingUsed(true); // 啟用千分位
String result = nf.format(x); // "23.55" (中文環境無千分位,美式環境有)
適用於需根據用戶 Locale 自動調整小數點符號、分組符號的國際化場景。
BigDecimal.setScale ~ 120 ns/op (含對象創建) DecimalFormat.format ~ 350 ns/op (共享實例) / 900 ns/op (新建) String.format ~ 400 ns/op NumberFormat.format ~ 300 ns/op
結論:高頻熱點路徑請用 BigDecimal 或手工整數運算(分為單位);顯示層用 DecimalFormat/String.format 即可。
new BigDecimal(0.1) → 0.10000000000000000555...,永遠用 valueOf 或字符串構造BigDecimal 或 long(分/厘)ArrayIndexOutOfBoundsException-0.001 格式化為 -0.00,業務敏感場景需額外判斷 abs(v) < 0.005 強制置 0BigDecimal 全程,最後 setScale(2, HALF_UP)String.format("%.2f", val) 或 DecimalFormat (ThreadLocal)NumberFormat.getCurrencyInstance(locale) 直接輸出幣種格式