从接口、数据库或用户输入拿到一个数字字符串,想输出成两位小数,直接对字符串做截取是错的做法——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 类型,不要存格式化后的字符串。