金融计算必须禁用float/double,因二进制无法精确表示0.1等金额,易致分币级错误;应统一用decimal(19,2)、bigdecimal(字符串构造)、整数单位(如“分”)运算。

金融计算必须避开 float 和 double,这不是优化建议,而是硬性要求。它们的二进制表示机制决定了像 0.1、0.01 这类常见金额根本无法精确存储,一次加减就可能引入误差,多次运算后误差累积,最终导致分币级错误——这在支付、对账、利息结算中是不可接受的。
金融场景一律禁用浮点类型
包括但不限于:订单金额、用户余额、税率、汇率换算、手续费、红包发放、账单明细。哪怕只做“显示”或“临时缓存”,只要原始数据来自 float/double,风险就已埋下。
- ❌ 错误写法:
double amount = 19.99;或float balance = user.getBalance(); - ✅ 正确起点:所有金额字段在数据库定义为
DECIMAL(19,2),Java 实体类对应BigDecimal,前端传参也应为字符串格式(如"19.99") - ⚠️ 特别注意:不要用
BigDecimal.valueOf(double)构造,它会继承 double 的误差;必须用字符串构造,如new BigDecimal("19.99")
用 BigDecimal 做高精度运算
BigDecimal 不是“更准一点的 double”,它是基于十进制的任意精度算术,能真正表达并精确计算“19.99 + 0.01 = 20.00”。关键操作要规范:
- 加减乘用
add()、subtract()、multiply(),结果天然精确 - 除法必须指定
scale和RoundingMode,例如:amount.divide(divisor, 2, RoundingMode.HALF_UP)(保留两位小数,四舍五入) - 比较用
compareTo(),不是equals()(后者会比较 scale,new BigDecimal("5")和new BigDecimal("5.00")equals 返回 false)
替代方案:整数单位运算(适合简单场景)
如果业务逻辑极轻量(如仅做找零、计数),可将金额全部转为最小货币单位(如“分”)用 long 运算:
- 19.99 元 → 1999 分(
long cents = Math.round(19.99 * 100);,注意这里19.99必须来自字符串解析,不能直接写 double 字面量) - 所有加减乘除都在整数域完成,完全规避浮点误差
- 展示时再除以 100.0 并格式化,但核心计算不碰小数
非金融场景可合理放宽
不是所有地方都要上 BigDecimal。以下场景 float/double 完全可用,且更高效:
- 图形渲染中的坐标、透明度(alpha=0.7f)、模型变换矩阵
- 传感器原始数据(GPS 经纬度、加速度计读数),设备硬件误差远大于浮点表示误差
- 机器学习训练中的梯度更新,框架本身依赖 float32 加速
- 音视频时间戳、缩放比例等对人眼/人耳不敏感的中间值











