1. 概述
在 Java 开发中,我们经常会遇到需要将 double
类型转换为 long
的场景,比如处理金额、时间戳或数学计算结果。虽然看似简单,但不同转换方式的行为差异可能在关键时刻导致“踩坑”。
本文将系统梳理几种常见的 double
到 long
的转换方法,并分析其精度丢失行为,帮助你在实际项目中做出合理选择。
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
提供了几个实用方法:round
、ceil
、floor
。它们能让你控制舍入方向。
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.ceil
和Math.floor
返回double
,但其值在可表示范围内等价于long
,使用时建议包裹一层(long)
转换。
5. 总结
在实际开发中,选择哪种 double
转 long
的方式,取决于你对精度和舍入行为的要求:
- ✅ 简单截断:用
(long)
或Double.longValue()
- ✅ 四舍五入:优先使用
Math.round()
- ✅ 向上/向下取整:使用
Math.ceil()
/Math.floor()
,记得转long
📌 建议:在涉及金额、计费、分页等关键逻辑时,务必明确舍入策略,避免因精度问题引发线上事故。
本文完整示例代码已托管至 GitHub:
👉 https://github.com/baeldung/core-java-modules/tree/master/core-java-numbers-3