Java中基本类型自动转换不报错但会丢失精度,如float转double仅扩展二进制表示,无法恢复原始精度,故d==12.8为false。

Java 中基本类型自动转换不会报错,但强转(尤其是向小范围或截断型类型转换)极易导致精度丢失或溢出——这不是 bug,是语言设计的必然结果。关键在于:什么时候该拦、什么时候该换、什么时候该用别的东西。
自动转换也会丢精度:float → double 不等于“更准”
很多人以为 float 赋值给 double 是安全提升,其实不然。浮点数本质是二进制近似表示,float 只有 23 位尾数,double 虽有 52 位,但无法“修复”原始 float 已丢失的精度。
-
float f = 12.8F;存储的其实是12.800000190734863这样的近似值 -
double d = f;只是把这个近似值原样扩展成double格式,不是“还原成 12.8” - 所以
d == 12.8为false,连Math.abs(d - 12.8) 都可能失败
真正安全的做法是:从字符串构造,比如 Double.valueOf("12.8") 或 new BigDecimal("12.8")。
强转截断 vs 四舍五入:别用 (int) d 取整
(int) 3.9 得到 3,(int) -3.9 得到 -3——这是向零截断,不是四舍五入。业务中多数场景需要的是四舍五入语义。
- 用
Math.round(d):返回long,对正负数都按“+0.5 后向下取整”规则处理 - 注意
Math.round(float)返回int,Math.round(double)返回long,类型不一致容易出错 - 若需转
int且防溢出,优先用Math.toIntExact(Math.round(d)),溢出时抛ArithmeticException
直接写 (int) Math.round(d) 看似省事,但一旦 Math.round(d) 返回超 int 范围的 long(比如 3000000000L),强转后就是静默错误值 -1294967296。
大整数转小整型:溢出比精度丢失更隐蔽
int 最大值是 2147483647,long 超过它再强转成 int,结果不是报错,而是绕回负数——(int) 2147483648L 得到 -2147483648。
- 用
Math.toIntExact(longValue)替代裸强转,溢出时立刻暴露问题 - 若业务允许饱和(overflow → 边界值),可用 Guava 的
Ints.saturatedCast(longValue) - 数据库主键、时间戳毫秒值、金额等字段,绝不能用
int接收long,哪怕“现在看起来够用”
一个订单 ID 是 long,你用 int orderId = (int) rs.getLong("id") 拿,第 2147483648 个订单就会变成负数 ID,下游系统很可能直接解析失败或查无此单。
什么时候必须放弃基本类型?
涉及金额、科学计算、配置阈值、金融利率等对精度敏感的场景,double 和 float 本身就是错误选择。
- 用
BigDecimal,且务必用字符串构造:new BigDecimal("19.99"),而非new BigDecimal(19.99) -
BigDecimal的doubleValue()会重新引入精度丢失,仅在必须交由遗留接口时才考虑 - 如果只是做比较或加减,
long单位(如“分”代替“元”)仍是轻量高效的选择,但要注意运算过程是否溢出
最常被忽略的一点:BigDecimal 的 equals() 会比较 scale(小数位数),new BigDecimal("1.0") 和 new BigDecimal("1.00") 不相等;要用 compareTo() 做数值比较。

















