math.round 不是传统四舍五入,而是对正数向上、负数向零取整(如 math.round(-2.5) = -2);需精确四舍五入时应使用 bigdecimal.setscale(小数位, roundingmode.half_up),且必须用字符串构造以避免精度误差。

Math.round 为什么有时四舍五入结果不对
Math.round 看似简单,但它的行为和日常理解的“四舍五入”有本质差异:它其实是“向正无穷方向舍入到最近的整数,.5 的情况向远离零的方向取整”。比如 Math.round(2.5) 得 3,Math.round(-2.5) 却得 -2 —— 这不是银行家舍入,也不是传统四舍五入,而是“四舍六入五成双”的反面。
常见错误现象:Math.round(1.5f) → 2,Math.round(-1.5f) → -1,导致负数场景下业务逻辑出错(比如金额扣减、评分计算)。
- 只适用于
float和double,且返回的是int或long,无法指定小数位数 - 底层调用的是
floor(x + 0.5)类似逻辑,对负数不友好 - 如果只是想取整(无小数位),且能接受该语义,它够快;否则别用它做“四舍五入”
BigDecimal 的 RoundingMode.HALF_UP 才是真四舍五入
要严格实现小学数学意义上的四舍五入(0–4舍、5–9入,不分正负),必须用 BigDecimal 配合 RoundingMode.HALF_UP。这是金融、计费等场景的标配做法。
使用场景:金额计算、税率保留两位小数、用户可见的精度控制。
立即学习“Java免费学习笔记(深入)”;
- 构造
BigDecimal时,**永远用字符串构造器**,比如new BigDecimal("12.345"),避免new BigDecimal(12.345)引入 double 二进制精度误差 - 调用
setScale(2, RoundingMode.HALF_UP)指定保留 2 位小数并四舍五入 -
RoundingMode.HALF_UP对1.5→2、-1.5→-2,符合直觉;而RoundingMode.HALF_DOWN是 1.5→1、-1.5→-1,容易混淆
示例:
BigDecimal value = new BigDecimal("3.14159");
BigDecimal rounded = value.setScale(2, RoundingMode.HALF_UP); // 结果为 3.14
性能与兼容性:什么时候不该用 BigDecimal
在高吞吐、低延迟场景(如实时风控引擎、高频日志聚合),BigDecimal 的对象创建开销和不可变性会成为瓶颈。此时若精度要求不高(比如统计 PV/UV 的近似值),可考虑预处理 + Math.round 配合缩放。
- 不要在循环内反复 new
BigDecimal字符串——提前缓存常量或改用整数运算(如把元转为分,全程用long计算) - Android 低版本(API BigDecimal 的某些
setScale行为有 bug,需加单元测试覆盖边界值 -
Math.round是 native 实现,比BigDecimal快 10–100 倍,但仅限于“整数截断”需求
容易踩的坑:RoundingMode 名字误导人
RoundingMode.HALF_UP 听起来像“一半向上”,但其实它就是标准四舍五入;真正容易误用的是 RoundingMode.HALF_EVEN(银行家舍入),它在 .5 时向偶数靠拢(1.5→2,2.5→2),用于统计学降偏,但业务系统里几乎没人真要这个。
-
RoundingMode.UP不是“向上四舍五入”,而是“只要非零就进一”,即 1.1→2,-1.1→-2 -
RoundingMode.CEILING是“向正无穷取整”,1.1→2,-1.1→-1,和UP不同 - 所有
RoundingMode常量名都不带“round”字样,靠名字猜行为极危险,必须查 Javadoc 或写测试验证
实际开发中,95% 的“四舍五入”需求只需要记住一条:用 new BigDecimal(x + "") + setScale(n, RoundingMode.HALF_UP)。其他模式,除非明确知道为什么选它,否则别碰。









