System.out.println(0.05 + 0.01); // 0.060000000000000005
System.out.println(1.0 - 0.42); // 0.5800000000000001
System.out.println(4.015 * 100); // 401.49999999999994
System.out.println(123.3 / 100); // 1.2329999999999999
float/double 採用 IEEE 754 二進制浮點數,無法精確表示許多十進制小數,導致財務、計量場景出現誤差。
DecimalFormat df = new DecimalFormat("0.00");
System.out.println(df.format(3.125)); // 3.12 (銀行家捨入:5 向偶數靠攏)
System.out.println(df.format(3.135)); // 3.14
預設 RoundingMode.HALF_EVEN 非商業慣用「四捨五入」,不可靠。
BigDecimal bd1 = new BigDecimal(0.05);
System.out.println(bd1); // 0.05000000000000000277555756156289135105907917022705078125
BigDecimal bd2 = new BigDecimal("0.05");
System.out.println(bd2); // 0.05 ✅
// 推薦:valueOf 內部調用 Double.toString
BigDecimal bd3 = BigDecimal.valueOf(0.05); // 等同於 new BigDecimal("0.05")
結論:只能用 String 或 valueOf(double) 構造,絕不可直接用 double 值。
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("5");
// ❌ 錯誤:a 不會變
a.add(b);
// ✅ 正確:必須接收返回值
BigDecimal c = a.add(b); // 加
BigDecimal d = a.subtract(b); // 減
BigDecimal e = a.multiply(b); // 乘
BigDecimal f = a.divide(b, 2, RoundingMode.HALF_UP); // 除(需指定精度與捨入模式)
BigDecimal 為不可變對象,每次運算產生新實例,類似 String。
public final class MoneyUtil {
private static final int SCALE = 2;
private static final RoundingMode MODE = RoundingMode.HALF_UP;
public static BigDecimal add(BigDecimal a, BigDecimal b) {
return a.add(b).setScale(SCALE, MODE);
}
// ... subtract, multiply, divide 同理
}
String 或 valueOf 構造。scale 與 RoundingMode 避免 ArithmeticException。