必须用字符串构造BigDecimal并显式调用setScale(2, RoundingMode.HALF_UP),否则double构造导致精度丢失,即使使用HALF_UP也无法纠正;负scale需谨慎用于整数位舍入,展示应统一走BigDecimal流程而非DecimalFormat。

要用 RoundingMode.HALF_UP 在金融计算中实现真正可靠的标准四舍五入,关键不是“用了这个模式就行”,而是必须配合正确的数值构造方式和 scale 控制——否则看似四舍五入,实则因 double 精度丢失导致结果偏差,这在金额场景下是不可接受的。
必须用字符串构造 BigDecimal
金融数据(如 19.995、123.4567)一旦用 new BigDecimal(19.995) 初始化,就会因 double 的二进制表示缺陷,实际存入的是近似值(比如 19.994999999999998),后续再怎么 setScale 也救不回来。正确做法是:
- 始终使用字符串字面量:
new BigDecimal("19.995") - 或从用户输入、数据库字段直接读取字符串再转 BigDecimal
- 避免任何涉及
double或float的中间转换
明确指定小数位数与舍入模式
金融场景通常要求保留两位小数(元为单位),调用 setScale(2, RoundingMode.HALF_UP) 即可。注意:
-
scale = 2表示保留小数点后两位,舍入依据是第三位是否 ≥ 5(即 ≥ 0.005) -
RoundingMode.HALF_UP对正负数统一处理:1.235 → 1.24,-1.235 → -1.24(符合会计直觉) - 不要用已废弃的
BigDecimal.ROUND_HALF_UP,改用RoundingMode.HALF_UP
整数位舍入也要小心(如千位取整)
某些报表或风控场景需按百元、千元汇总,这时用负数 scale:
-
value.setScale(-2, RoundingMode.HALF_UP):以百为单位舍入(如 1249 → 1200,1250 → 1300) - 原理是判断余数是否 ≥ 50(即单位的一半),不是简单看个位或十位数字
- 同样必须基于精确的 BigDecimal 字符串构造,否则整数部分也可能失真
避免混用 DecimalFormat 或 String.format
虽然 DecimalFormat 和 String.format("%.2f") 默认用 HALF_UP,但它们底层仍依赖 double 运算,对边界值(如 0.075、2.505)易出错:
- 例如
String.format("%.2f", 0.075)有时输出 "0.07" 而非 "0.08" - 这不是 bug,而是 double 无法精确表示 0.075 导致的连锁误差
- 金融逻辑中所有中间计算和最终展示,都应统一走 BigDecimal 流程

















