Java float/double 二進制浮點數無法精確表示十進制小數(如 0.1),財務、電商等場景必須使用 BigDecimal。本文總結格式化輸出、四捨五入模式、精確算術封裝及常用工具方法。
double d = 3.1415926;
// 1. DecimalFormat
String s1 = new DecimalFormat("0.00").format(d); // "3.14"
String s2 = new DecimalFormat("#.00").format(d); // "3.14"
// 2. String.format
String s3 = String.format("%.2f", d); // "3.14"
// 3. BigDecimal 四捨五入
BigDecimal bd = new BigDecimal(d);
double rounded = bd.setScale(2, RoundingMode.HALF_UP).doubleValue(); // 3.14
public class PreciseCompute {
private static final int DEF_DIV_SCALE = 10;
public static double add(double v1, double v2) {
return new BigDecimal(Double.toString(v1)).add(new BigDecimal(Double.toString(v2))).doubleValue();
}
public static double sub(double v1, double v2) {
return new BigDecimal(Double.toString(v1)).subtract(new BigDecimal(Double.toString(v2))).doubleValue();
}
public static double mul(double v1, double v2) {
return new BigDecimal(Double.toString(v1)).multiply(new BigDecimal(Double.toString(v2))).doubleValue();
}
public static double div(double v1, double v2) { return div(v1, v2, DEF_DIV_SCALE); }
public static double div(double v1, double v2, int scale) {
if (scale < 0) throw new IllegalArgumentException("Scale must be >= 0");
return new BigDecimal(Double.toString(v1)).divide(new BigDecimal(Double.toString(v2)), scale, RoundingMode.HALF_UP).doubleValue();
}
public static double round(double v, int scale) {
if (scale < 0) throw new IllegalArgumentException("Scale must be >= 0");
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
}
}
關鍵點:構造器傳 Double.toString(v) 而非直接 v,避免二進制誤差帶入;除法指定精度與舍入模式。
private BigDecimal toBigDecimal(Object obj) {
String val = String.valueOf(obj).replaceAll(",", "");
if (!isNumber(val)) return BigDecimal.ZERO;
return new BigDecimal(val).setScale(2, RoundingMode.HALF_UP);
}
private String formatWithComma(Object obj) {
BigDecimal bd = toBigDecimal(obj);
DecimalFormat df = new DecimalFormat("#,###.00");
String s = df.format(bd);
return s.startsWith(".") ? "0" + s : s; // 處理 .00 -> 0.00
}
private boolean isNumber(String s) {
try { Double.parseDouble(s); return true; } catch (NumberFormatException e) { return false; }
}
| 模式 | 行為 | 示例 (1.555 -> 2位) |
|---|---|---|
HALF_UP | 四捨五入 | 1.56 |
HALF_DOWN | 五捨六入 | 1.55 |
HALF_EVEN | 銀行家算法(向偶數靠攏) | 1.56 |
UP | 遠離零方向 | 1.56 |
DOWN | 趨向零方向 | 1.55 |
CEILING | 向正無窮 | 1.56 |
FLOOR | 向負無窮 | 1.55 |