核心是将隐式截断转为显式、可控、符合业务语义的取整:金额换算用math.round(d×100),分页用math.floor,门槛判断用math.ceil;源头防精度污染,优先用bigdecimal字符串构造;安全转换推荐math.tointexact(math.round(d)),并辅以静态检查与边界测试。

防范 Java 中 double 强转为 int 时小数位直接丢失,核心不是“避免转换”,而是把隐式截断变成显式、可控、符合业务语义的取整行为。
明确业务意图,选对取整方式
强制转 int 总是向零截断(3.9 → 3,-3.9 → -3),但业务需要的往往不是这个结果:
- 金额换算(元→分):必须四舍五入 → 用
(int) Math.round(d * 100),别用(int)(d * 100) - 分页总数计算:
total / pageSize应向下取整 → 用(int) Math.floor(d) - 门槛达标判断(如“满3.2件享折扣”):需向上取整 → 用
(int) Math.ceil(d) - 纯整数输入校验(如用户填的件数):后端应解析原始字符串,而非接收 double 再转 int
堵住上游精度污染源头
很多错账不是转换写错了,而是传进来的 double 本身已失真:
- Excel 导入:Apache POI 默认返回
double,2.3 可能存为2.2999999999999996→ 改用BigDecimal.valueOf(cell.getNumericCellValue()).setScale(2, RoundingMode.HALF_UP).intValue() - JSON 解析:Jackson 默认把数字当
Double→ 配置DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS - 数据库字段:金额别用
DOUBLE,改用DECIMAL(10,2);若无法改,查出后立刻用new BigDecimal(rs.getString("amount"))
用安全方法替代裸强转
裸括号强转既不报错也不提醒,风险静默积累:
- 要四舍五入:统一用
Math.round(d)(返回long),再转int,注意范围是否溢出 - 要防溢出:大数转小类型时,用
Math.toIntExact((long) Math.round(d)),超范围直接抛异常 - 精度敏感场景(如计费):全程用
BigDecimal,构造时用字符串(new BigDecimal("299.99")),不用double构造器 - 加日志监控:在关键转换点打印原始值、转换值、差值,差值持续在
0.4–0.9区间就是典型错账信号
建立代码审查与自动化防护
单靠开发者自觉容易遗漏,需机制兜底:
- 静态检查:用 SonarQube 或 PMD 规则拦截所有
(int)xxx和.intValue(),除非带明确注释说明为何可截断 - 单元测试覆盖边界:重点测
0.49999999999999994、0.5、0.5000000000000001这类 IEEE 754 表示临界值 - 关键服务上线前做“浮点审计”:扫描全部
double类型参数、字段、返回值,确认是否真需浮点,能否改用整型单位(如分、毫秒、像素)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











