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 二進制浮點表示,無法精確表達許多十進制小數。Math.round、DecimalFormat 皆因底層運算誤差而失效。
鐵律:只能用 String 建構子:
new BigDecimal("0.1") // 精確 0.1
new BigDecimal(0.1) // 實際 0.10000000000000000555...
Effective Java》同樣建議商業運算必須用 BigDecimal(String)。
public class Arith {
private static final int DEF_DIV_SCALE = 10;
private Arith() {}
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) { ... }
public static double mul(double v1, double v2) { ... }
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(...);
return new BigDecimal(Double.toString(v1))
.divide(new BigDecimal(Double.toString(v2)), scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
public static double round(double v, int scale) {
if (scale < 0) throw new IllegalArgumentException(...);
return new BigDecimal(Double.toString(v))
.divide(new BigDecimal("1"), scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
使用方式:
double result = Arith.add(0.05, 0.01); // 0.06
double rounded = Arith.round(4.015, 2); // 4.02
double → Double.toString() → BigDecimal(String) 避免二進制誤差殘留。scale 與 ROUND_HALF_UP 處理無限循環小數。金融、電商、計價、報表等任何涉及十進制精確運算的商業邏輯。科學/工程計算仍可用 double。