1. 概述

在 Java 开发中,我们经常会遇到需要将 double 类型转换为 long 的场景,比如处理金额、时间戳或数学计算结果。虽然看似简单,但不同转换方式的行为差异可能在关键时刻导致“踩坑”。

本文将系统梳理几种常见的 doublelong 的转换方法,并分析其精度丢失行为,帮助你在实际项目中做出合理选择。

2. 使用类型强制转换(Type Casting)

最直接的方式就是使用强制类型转换操作符 (long)

Assert.assertEquals(9999, (long) 9999.999);

特点

  • 简单粗暴,性能最好
  • 属于窄化基本类型转换(narrowing primitive conversion)
  • 直接截断小数部分,不进行四舍五入

⚠️ 注意9999.999 转换后变成 9999,丢失了 .999 部分。这种“截断式”转换在某些业务场景下可能是陷阱,比如金额处理。

3. 使用 Double.longValue()

如果 double 是以包装类 Double 形式存在,可以直接调用其 longValue() 方法:

Assert.assertEquals(9999, Double.valueOf(9999.999).longValue());

内部机制
该方法本质上也是执行了 (long) 强制转换,行为与第 2 节完全一致。

📌 结论

  • Double.longValue()(long) 强制转换
  • 底层实现就是截断小数位,不会四舍五入

如果你期望的是精确舍入,这种方式并不适合。

4. 使用 Math 工具类方法

当需要更精细的舍入策略时,java.lang.Math 提供了几个实用方法:roundceilfloor。它们能让你控制舍入方向。

4.1 Math.round()

返回最接近参数的 long 值,遵循“四舍五入”规则:

Assert.assertEquals(9999, Math.round(9999.0));
Assert.assertEquals(9999, Math.round(9999.444));  // 小于0.5,舍去
Assert.assertEquals(10000, Math.round(9999.999)); // 大于等于0.5,进位

优点:符合日常数学中的四舍五入直觉
注意:返回类型是 long,但方法重载中也有返回 int 的版本,需注意参数范围避免溢出

4.2 Math.ceil()

向上取整,返回大于等于该值的最小整数(返回类型为 double):

Assert.assertEquals(9999, Math.ceil(9999.0), 0);
Assert.assertEquals(10000, Math.ceil(9999.444), 0);
Assert.assertEquals(10000, Math.ceil(9999.999), 0);

📌 使用场景

  • 分页计算中确保不丢数据
  • 资源预估时保守取值

⚠️ 返回值是 double,需要再转 long 才能赋值给 long 变量,例如:(long) Math.ceil(value)

4.3 Math.floor()

向下取整,返回小于等于该值的最大整数(返回类型也为 double):

Assert.assertEquals(9999, Math.floor(9999.0), 0);
Assert.assertEquals(9999, Math.floor(9999.444), 0);
Assert.assertEquals(9999, Math.floor(9999.999), 0);

📌 使用场景

  • 时间戳处理中去除毫秒部分
  • 数值分桶(bucketing)时归类

同样,返回值是 double,需显式转为 long

✅ 方法对比总结

方法 行为 返回类型 是否四舍五入
(long) 强制转换 截断小数 long
Double.longValue() 截断小数 long
Math.round() 四舍五入 long
Math.ceil() 向上取整 double
Math.floor() 向下取整 double

⚠️ 注意:Math.ceilMath.floor 返回 double,但其值在可表示范围内等价于 long,使用时建议包裹一层 (long) 转换。

5. 总结

在实际开发中,选择哪种 doublelong 的方式,取决于你对精度和舍入行为的要求:

  • 简单截断:用 (long)Double.longValue()
  • 四舍五入:优先使用 Math.round()
  • 向上/向下取整:使用 Math.ceil() / Math.floor(),记得转 long

📌 建议:在涉及金额、计费、分页等关键逻辑时,务必明确舍入策略,避免因精度问题引发线上事故。

本文完整示例代码已托管至 GitHub:
👉 https://github.com/baeldung/core-java-modules/tree/master/core-java-numbers-3


原始标题:Convert Double to Long in Java