财务系统禁用float/double,因其二进制无法精确表示十进制小数(如0.1),存储即失真、运算必累积误差;单次误差虽微,但多轮计算、对账比对、跨系统协同中会放大,导致结果不可复现,违反财务零偏差要求。

财务系统不能用 float/double,根本不是代码写得不够严谨,而是它们从诞生起就拒绝“精确表达十进制小数”——0.1 元、0.01 元、19.99 元,在二进制里全是无限循环小数,存储即失真,运算必累积。
十进制金额在二进制中天然存不准
计算机用二进制表示数字,而日常金钱是十进制的。就像 1/3 在十进制中写成 0.333… 永远除不尽一样,0.1 在二进制中是 0.0001100110011…₂ ——无限循环。float(32 位)和 double(64 位)只能截断保存,一定义义就带误差:
- double x = 0.1; 这行代码执行完,内存里存的已不是 0.1,而是最接近它的二进制近似值
- 哪怕只是声明一个金额变量,精度损失已经发生;后续所有加减乘除都在这个“有偏差的起点”上继续计算
微小误差会在业务逻辑中快速放大
单次误差可能只有 1e−17 元,但财务场景常含多轮、多路径、累积性操作,误差会叠加甚至畸变:
- 1.00 − 9 × 0.10 得到 0.09999999999999998,少算 0.00000000000000002 元
- 循环扣减(如分次退款、分期还款、积分抵扣):余额从 1.00 元开始,每轮减 0.10 元,第 4 轮后本该剩 0.40 元,实际可能变成 0.3999999999999999
- 对账比对时 0.1f + 0.2f != 0.3f,一个 == 判断直接失败,自动化对账流程中断
所谓“精度”不等于“小数点后两位安全”
float 约 7 位有效数字,double 约 16 位——但这指的是整个数字的有效位,不是小数部分保障位数:
- 金额 12345678.99 元,已逼近 float 的整数精度上限(±16777216),再做四舍五入或乘法极易跳变
- 用 double 存 12.35,实际可能是 12.34444444444444449;若转 BigDecimal 再 round(2),反而错进错出
- MySQL 中 FLOAT/DOUBLE 插入 2.888888,可能自动存为 2.889——存取不一致,连数据库层都不可信
财务场景容不得“几乎正确”
银行账户、支付清分、发票开票、分润结算等环节,要求结果可复现、零偏差、全链路一致:
- 单笔误差 0.00000001 元,百万级交易后偏差可达数百元
- 不同系统用不同语言处理同一笔账:Java 用 double、Python 用 float、前端 JS 用 Number,结果各不相同,对账无法闭环
- 审计与合规要求“确定性”:不能解释为“机器误差”,必须能给出完全一致、可验证的计算路径











