Java浮点数因IEEE 754二进制表示导致精度问题,应依场景选型:金融用BigDecimal(字符串构造、方法运算、compareTo比较),金额等固定精度用整数单位(如“分”),浮点比较须用EPSILON误差范围。

Java 中浮点数(float 和 double)用 IEEE 754 标准存储,本质是二进制近似表示十进制小数,导致很多看似简单的计算出现意外结果,比如 0.1 + 0.2 != 0.3。这不是 Java 的 Bug,而是所有遵循该标准的编程语言共有的底层限制。解决问题的关键不是“修复浮点数”,而是根据场景选择合适的数据类型和计算策略。
用 BigDecimal 替代 double 做精确十进制运算
金融、会计、价格计算等对精度零容忍的场景,必须避免 float/double。BigDecimal 是首选——它以字符串或整数构造,内部用整数比例模拟十进制,不引入二进制舍入误差。
- 永远用
new BigDecimal("0.1")或BigDecimal.valueOf(0.1)构造,绝不用new BigDecimal(0.1)(后者会先让 double 出错再包装) - 所有算术操作用方法:add()、subtract()、multiply()、divide(),其中 divide() 必须指定
MathContext或RoundingMode(如divide(other, 2, RoundingMode.HALF_UP)) - 比较用
compareTo()(返回 -1/0/1),不用equals()(后者会比较 scale,new BigDecimal("1.0").equals(new BigDecimal("1"))返回 false)
用整数单位规避小数计算
对固定精度的业务(如金额以“分”为单位、时间以“毫秒”为单位),直接用 long 或 int 存储整数倍,彻底绕开浮点数。
- 价格统一存为“分”:19.99 元 → 1999 分,所有加减乘除都在整数域完成
- 需要显示时再除以 100,并用
String.format("%.2f", amount / 100.0)或DecimalFormat格式化(注意:此处仅用于展示,不参与计算) - 优势:无精度损失、性能高、逻辑清晰;缺点:需人工管理单位换算,不适合动态精度场景
浮点数比较必须用误差范围(EPSILON)
当必须使用 double(如科学计算、图形渲染),绝不能用 == 直接比较两个浮点数是否相等。
立即学习“Java免费学习笔记(深入)”;
- 定义一个足够小的阈值,例如
final double EPSILON = 1e-10; - 判断相等:
Math.abs(a - b) - 判断大小:用
a - b > EPSILON代替a > b,用b - a > EPSILON代替a - 注意:EPSILON 要与数值量级匹配,大数(如 1e10)可能需要更大 epsilon,可考虑相对误差
Math.abs(a - b)
避免累积误差:重设计算顺序与中间变量
多个浮点数连续运算时,误差会传播放大。可通过调整算法结构降低影响。
- 求和时优先用
Math.fma()(融合乘加,JDK9+),或使用 Kahan 求和算法补偿舍入误差 - 避免
a / b * c这类易溢出/失精度写法,改用a * c / b(若数学上等价且更安全) - 对数组统计,先排序再从小到大累加,减少小数被大数“吞掉”的风险
- 关键中间结果及时转成 BigDecimal 或整数暂存,尤其在循环中反复叠加时


















