浮点与金额精度是什么
浮点数不能精确表示多数十进制小数:0.1 加 0.2 得到 0.30000000000000004。金额与计费场景要用最小货币单位整数或十进制类型(Java BigDecimal、Python Decimal),并用舍入用例验证业务约定;精度问题一旦进入账务,对账与退款成本远高于测试成本。
基本信息
| 现象 | 0.1 加 0.2 不等于 0.3(二进制浮点表示误差) |
|---|---|
| 处理方式 | 最小货币单位整数、Decimal 与 BigDecimal 等十进制类型 |
| 舍入规则 | 四舍五入与银行家舍入结果不同,按业务约定断言 |
| 比较方式 | 容差比较(approx、delta),避免等号 |
| 高危场景 | 金额、计费、税率、折扣与积分累计 |
现象与处理
现象与处理
# 现象
0.1 + 0.2 # 0.30000000000000004
# 处理(Python)
from decimal import Decimal
Decimal('0.1') + Decimal('0.2') # Decimal('0.3')
# 断言(pytest)
from pytest import approx
assert 0.1 + 0.2 == approx(0.3)典型使用场景
- 金额计算:订单、退款、优惠分摊的精确结果。
- 税率与折扣:百分比相乘后的舍入边界。
- 积分与余额:累计加减中误差不放大。
- 报表对账:总额与明细逐层分摊后仍对平。
- 跨语言接口:下游以浮点接收时的精度损失。
- 落库校验:DECIMAL 字段精度与代码层口径一致。
常见误区与正解
| 误区 | 后果 | 正解 |
|---|---|---|
| float 存金额 | 累加出现分币误差 | 整数分或 Decimal |
| 等号比较浮点 | 偶发失败 | 容差比较 |
| 期望值同式算法 | 误差双向抵消 | 手算或独立来源 |
| 舍入规则想当然 | 与对账系统对不上 | 先确认业务约定 |
测试舍入:银行家舍入与四舍五入结果不同,需按业务约定断言分位值。
数据库也要对得上:DECIMAL 字段精度与代码层 Decimal 保持同一口径,落库前后各断言一次。
整数分是常见落法:内部用分存储与计算,展示层再格式化成带小数的货币字符串。
断言建议
- 容差贴合业务精度:金额用分或厘级容差,比例指标可适当放宽。
- 期望值用独立来源:手算样例、账务系统导出或外部计算器。
- 多层分摊逐级断言:总额、分摊、明细三段各自校验。