java大数运算需用bigdecimal规避float/double精度丢失,关键在于字符串构造、全程隔离double、乘除指定舍入模式及序列化防精度丢失。

Java 中大数运算用 BigDecimal 解决精度丢失,关键不在“用了没”,而在“怎么用”。浮点数精度丢失是 float/double 的固有缺陷——0.1 在二进制中本就是无限循环小数,而 BigDecimal 的作用不是修复它,而是绕过它:用字符串表示十进制字面量,再用整数逻辑做运算。
构造阶段:堵住精度污染的第一道口
所有后续误差,几乎都源于错误的初始化。
- ✅ 优先用字符串构造:new BigDecimal("19.99") —— 十进制字面量被原样解析,无任何转换损耗
- ✅ 次选 valueOf:BigDecimal.valueOf(19.99) —— 内部调用
Double.toString(),生成最短可读十进制表示(如不输出 "1.999E1") - ❌ 绝对禁用 double 构造:new BigDecimal(19.99) —— 此时 19.99 已是近似值(实际存的是 19.990000000000002),BigDecimal 只是把它“精确地记错”
- ⚠️ 注意:
String.valueOf(double)不可靠,可能触发科学计数法;必须用Double.toString(d)才安全
数据流转:全程隔离 double 类型
从前端到数据库,只要中间出现一次 double,精度就可能被悄悄污染。
- 前端传金额时,应传字符串
"19.99",后端直接new BigDecimal(param) - 数据库字段必须定义为
DECIMAL(18,2)或类似定点类型,而非 FLOAT/DOUBLE - JDBC 查询时,用
rs.getBigDecimal("amount");写入时用ps.setBigDecimal(1, bd) - 避免
rs.getDouble()→new BigDecimal(double)这类“先失真、再封装”的反模式
运算阶段:加减保精度,乘除必控舍入
加减法在标度一致时天然不丢精度;但乘除会改变小数位数,不干预就会失控或抛异常。
-
add()/subtract():可直接调用,结果标度取两操作数中较大者 -
multiply():建议用双参版本,例如a.multiply(b, new MathContext(10, RoundingMode.HALF_UP)) -
divide():必须指定标度和舍入模式,如a.divide(b, 2, RoundingMode.HALF_EVEN)(银行家舍入,金融常用) - 不指定舍入时,
divide()遇 1÷3 这类情况会直接抛ArithmeticException
序列化与展示:别让 JSON 或日志把精度吃掉
即使内部计算全对,输出环节出错也会前功尽弃。
- FastJSON:启用
SerializerFeature.WriteBigDecimalAsPlain,防止输出成1.0123→1.123(吞掉小数点后零) - Jackson:配置
WRITE_BIGDECIMAL_AS_PLAIN,避免科学计数法或自动转 double - 前端接收时,不要用
parseFloat解析 BigDecimal 字符串;应原样展示,或用big.js等高精度库处理 - 调试打印时,用
toString(),别用doubleValue()—— 后者会再次引入二进制误差
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











