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

Java 保留 Double 兩位小數的 4 種主流方法對比

     編輯:Java 相關知識

核心結論先行

方法適用場景四捨五入返回類型線程安全推薦指數
BigDecimal金融/精確計算可控模式BigDecimal/double是⭐⭐⭐⭐⭐
DecimalFormat顯示/序列化預設半捨入String否(共享實例)⭐⭐⭐⭐
String.format日誌/快速輸出半捨入String是⭐⭐⭐
NumberFormat本地化顯示半捨入String否⭐⭐

方法一:BigDecimal —— 金融級精確控制

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 類型,勿轉回 double

方法二:DecimalFormat —— 格式化輸出首選

double 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 或每次新建實例。

方法三:String.format / System.out.printf —— 簡單快捷

double d = 3.1415926;
String s = String.format("%.2f", d);        // "3.14"
System.out.printf("價格: %.2f 元%n", d);     // 直接輸出

// 格式化語法:%[參數][標誌][寬度][.精度]轉換符
// %.2f  →  保留 2 位小數的浮點數
// %,.2f →  含千分位分隔符

底層調用 Formatter,線程安全,性能一般,適合日誌、報表輸出。

方法四:NumberFormat.getNumberInstance() —— 本地化感知

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 自動調整小數點符號、分組符號的國際化場景。

性能微基準參考 (JMH, 10^7 次調用)

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 即可。

常見坑點避雷

  1. double 構造 BigDecimal 陷阱:new BigDecimal(0.1) → 0.10000000000000000555...,永遠用 valueOf 或字符串構造
  2. 浮點數本質無法精確表示 0.1:任何格式化只是「顯示層修飾」,計算層請用 BigDecimal 或 long(分/厘)
  3. DecimalFormat 線程不安全:並發格式化會導致內部字段競爭,輸出亂碼或拋出 ArrayIndexOutOfBoundsException
  4. 負零問題:-0.001 格式化為 -0.00,業務敏感場景需額外判斷 abs(v) < 0.005 強制置 0

總結建議

  • 計算/存儲:BigDecimal 全程,最後 setScale(2, HALF_UP)
  • API 返回/日誌/前端展示:String.format("%.2f", val) 或 DecimalFormat (ThreadLocal)
  • 國際化報表:NumberFormat.getCurrencyInstance(locale) 直接輸出幣種格式
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved