java浮点数精度问题源于ieee 754二进制表示与十进制小数的结构性矛盾,解决需选用bigdecimal并严格遵循字符串构造、指定除法精度与舍入模式、用compareto()比较、非金融场景可采用整数放大法。

Java 浮点数计算精度问题不是 bug,而是 IEEE 754 二进制表示与十进制小数之间的结构性矛盾。0.1 在二进制中是无限循环小数(0.0001100110011…),double 只能截断存储,导致 0.1 + 0.2 = 0.30000000000000004 这类偏差。关键不在于“怎么修”,而在于“在哪用、怎么选、怎么避”。
BigDecimal 构造必须用字符串
这是最常踩的坑:用 new BigDecimal(0.1) 实际上传入的是已失真的 double 值,精度从第一步就丢了。正确做法只有两种:
-
✅ 推荐:
new BigDecimal("0.1")—— 字符串直接解析,无中间转换,零误差 -
✓ 可接受:
BigDecimal.valueOf(0.1)—— 内部调用Double.toString()转字符串再构造,比直接传 double 安全,但不如纯字符串可靠 -
❌ 绝对避免:
new BigDecimal(0.1)或new BigDecimal(Double.toString(0.1))—— 后者仍依赖原始 double 的不精确表示
除法必须指定精度和舍入模式
BigDecimal 的 divide() 方法若不指定参数,遇到 1 ÷ 3 这类无限小数会抛 ArithmeticException。金融场景常用配置:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 保留两位小数(如金额):
a.divide(b, 2, RoundingMode.HALF_UP) - 银行系统偏好更公平的统计舍入:
RoundingMode.HALF_EVEN(银行家舍入) - 避免用
RoundingMode.UNNECESSARY,除非你 100% 确认结果为有限小数
比较必须用 compareTo(),不用 equals()
equals() 比较的是值 + 小数位数(scale),new BigDecimal("1.0").equals(new BigDecimal("1")) 返回 false;而 compareTo() 只比数值大小:
-
bd1.compareTo(bd2) == 0→ 数值相等 -
bd1.compareTo(bd2) > 0→ bd1 更大 - 永远别写
bd1.equals(bd2)来判断数值是否相同
非金融场景可考虑整数放大法
当性能敏感、且业务允许固定小数位(如温度保留 1 位、传感器读数保留 3 位),用整数替代浮点运算更轻量:
- 例如金额统一存“分”:
int totalCents = 1999 + 2000;→ 3999 分 = 39.99 元 - 科学数据放大 1000 倍:
long valueNano = Math.round(original * 1000);(注意用Math.round避免截断误差) - 风险点:放大倍数过大可能溢出 long,建议配合 scale 参数做边界校验
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










