从接口、数据库或用户输入拿到一个数字字符串,想输出成两位小数,直接对字符串做截取是错的做法——1.999 截成 1.99,1 变成 1.0,两种情况都不对。正确思路是先把字符串转成数字,再格式化。

方法一:String.format

String s = "3.14159";
double d = Double.parseDouble(s);
String r = String.format("%.2f", d); // 3.14

简单直接,缺点是默认四舍五入用的 HALF_UP,且格式依赖当前语言环境,在部分区域设置下小数点会变成逗号。需要固定格式时加上 Locale:

String r = String.format(Locale.US, "%.2f", d);

方法二:BigDecimal 的 setScale

金额计算必须用 BigDecimal,避免 double 精度问题:

BigDecimal bd = new BigDecimal("3.145");
String r = bd.setScale(2, RoundingMode.HALF_UP).toPlainString(); // 3.15

注意构造时要用字符串参数,用 new BigDecimal(3.145) 传 double 已经把误差带进去了。toPlainString 避免大数被输出成科学计数法。

方法三:DecimalFormat

DecimalFormat df = new DecimalFormat("0.00");
df.setRoundingMode(RoundingMode.HALF_UP);
String r = df.format(Double.parseDouble(s));

DecimalFormat 默认是 HALF_EVEN(银行家舍入),2.125 会变成 2.12,业务上要四舍五入就得手动设 HALF_UP。它也不是线程安全的,别做成静态共享实例。

字符串直接处理要小心

有些场景确实只能动字符串,比如数值特别大、超出 double 精度范围。思路是找到小数点,按规则判断第三位决定是否进位,处理进位时可能连锁向前进,实现麻烦且容易出错。能不能用 BigDecimal 就尽量用。

几个坑

  • 空字符串和 null 要先判空,字符串带空格会抛 NumberFormatException;
  • 带千分位逗号的字符串不能直接 parseDouble,要先去掉逗号;
  • 保留两位显示和保存两位是两个需求,存储用 DECIMAL 类型,不要存格式化后的字符串。