金额显示两位小数,写 Java 的人几乎都写过,但写法五花八门,结果还常常不一样:2.345 一会儿变 2.34,一会儿变 2.35。原因在于「保留」这个词包含两件事——数值怎么舍入,字符串怎么显示。
只是显示:String.format
String s = String.format("%.2f", 2.345); // 2.34这是最省事的写法,但它受 Java 浮点表示影响。2.345 在二进制里其实略小于 2.345,所以舍入得到 2.34。另外它默认使用银行家舍入以外的规则,结果和业务上的「四舍五入」偶尔对不上。还有一点,%.2f 会顺手加上千分位以外的格式,负数也会带符号,做前端展示要再处理。
要精确计算:BigDecimal
BigDecimal b = new BigDecimal("2.345");
b = b.setScale(2, RoundingMode.HALF_UP); // 2.35金额类计算必须用这个。注意构造参数一定要传字符串,不能传 double,new BigDecimal(2.345) 会把浮点的误差一起带进来,setScale 之后还是错的。
RoundingMode.HALF_UP 是真正的四舍五入,遇到 5 就进位;HALF_EVEN 是银行家舍入,2.345 变 2.34,2.355 变 2.36。财务系统有的规范要求用后者,用哪个得看业务约定。
格式化输出:DecimalFormat
DecimalFormat df = new DecimalFormat("#,##0.00");
df.format(1234.5); // 1,234.50它适合报表和导出,能顺便加千分位。但 DecimalFormat 不是线程安全的,放在静态变量里被多个线程共用,结果是错乱甚至抛异常,要么每次 new 一个,要么加锁。
其他几个常被问到的写法
- Math.round(x * 100) / 100.0:简单粗暴,缺点是结果还是 double,末尾可能跟着 0.0000000000001 这样的尾巴,而且对大数会溢出。
- 乘 100 后强转 int:直接丢精度,不是四舍五入,金额场景千万别用。
- 返回 double 还是 String:对外接口建议返回字符串或者以分为单位的整数,避免前端再解析时又丢一次精度。
一句话总结:展示用 String.format 或 DecimalFormat,算钱一律 BigDecimal 加字符串构造,舍入模式按业务规定选 HALF_UP 还是 HALF_EVEN。