Java浮点数精度丢失源于二进制无法精确表示十进制小数,应使用BigDecimal从源头规避:字符串或valueOf构造、数据库用DECIMAL、运算时规范标度与舍入、JSON序列化启用Plain输出。

Java浮点数精度丢失不是代码写错了,而是 float/double 本身无法精确表示很多十进制小数(比如 0.1、0.2),这是二进制存储机制决定的。想真正解决问题,不能靠“四舍五入”掩盖,得用 BigDecimal 从源头绕过浮点缺陷——它不修复 float,而是彻底不用它。
创建时就守住精度底线
90% 的精度问题,其实发生在第一步:把数字变成 BigDecimal 的那一刻。
- ✅ 用字符串构造:
new BigDecimal("19.99")—— 原样读入,零误差 - ✅ 用 valueOf:
BigDecimal.valueOf(19.99)—— 内部自动转成字符串再构造,安全又简洁 - ❌ 别用 double 直接构造:
new BigDecimal(19.99)—— 此时 19.99 已是近似值,错误被“精确固化” - ⚠️ 注意:
String.valueOf(d)可能输出科学计数法(如 "1.23E5"),应改用Double.toString(d)配合构造,但不如前两者直接可靠
数据库交互别让 double 插队
后端和数据库之间,最容易悄悄混入 double,让 BigDecimal 形同虚设。
- 数据库字段必须用 DECIMAL(如
DECIMAL(18,2)),禁用 FLOAT/DOUBLE - 读取时调用
rs.getBigDecimal("amount"),别先getDouble()再转 - 写入时用
ps.setBigDecimal(1, amount),确保 JDBC 驱动直通定点数
运算规则要主动掌控
加减法相对友好,乘除则必须人工干预标度和舍入,否则结果不可控。
立即学习“Java免费学习笔记(深入)”;
- 加减:
a.add(b)、a.subtract(b)—— 标度自动取大,一般无需额外处理 - 乘法:
a.multiply(b, new MathContext(10, RoundingMode.HALF_UP))—— 推荐带 MathContext,防位数爆炸 - 除法:
a.divide(b, 2, RoundingMode.HALF_EVEN)—— 必须指定小数位和舍入模式,否则可能抛异常 - 统一收口:
result.setScale(2, RoundingMode.HALF_EVEN)放在最后一步,确保输出格式符合业务要求(如金额保留两位)
JSON序列化别让前端“看走眼”
后端传出去的 "10.00",如果 JSON 库默认调用 toString(),可能变成 10 或 1E1,前端解析就变样了。
- FastJSON:启用
SerializerFeature.WriteBigDecimalAsPlain - Jackson:配置
SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN - 原理一致:强制调用
toPlainString(),保证输出是干净的十进制字符串,不含科学计数法,小数点后零不丢


















