float不能用于货币计算,根本原因是其二进制浮点表示无法精确表达大多数十进制小数(如0.1),ieee 754标准下尾数位数有限导致截断误差,且误差在多次运算中累积放大;正确方案是用bigdecimal(字符串构造)或整数“分”单位存储。

不能用于货币计算,根本原因在于 float 类型无法精确表示大多数十进制小数——这不是 Java 的缺陷,而是二进制浮点数的数学本质决定的。
float 为什么存不准 0.1?
0.1 在十进制中看起来简单,但在二进制中是无限循环小数:
0.1(十进制) = 0.0001100110011…₂(二进制),永远除不尽。
float 只有 23 位尾数(单精度),必须截断,于是刚一赋值就带上了误差。
- 写 float f = 0.1f; 实际存的是 ≈ 0.10000000149011612
- 0.1f + 0.2f 得到 0.30000001192092896,不是 0.3
- 10.0f - 0.9f * 10 结果是 1.0000002,而非精确的 1.0
误差会滚雪球式放大
单次误差微小,但金融场景常涉及多次运算:累加、分润、扣减、复利等。误差不抵消,只累积。
- 循环扣减 0.9 元共 10 次,余额本应为 1.0,float 却算出 1.0000002
- 百万笔 0.0001 元佣金累加,理论应收 100 元,float 可能得 99.9999999999986
- 数据库存取时若用 FLOAT 字段,插入 2.888888 可能自动变成 2.889,导致前后端不一致
正确替代方案有哪些?
核心原则:用整数或十进制精确类型,避开二进制浮点。
-
最推荐:BigDecimal(字符串构造)
new BigDecimal("19.99") —— 安全;
new BigDecimal(19.99) —— 危险(先用 double 存错再转) -
高性价比:long / int 存“分”
19.99 元 → 存 1999(单位:分),全程整数运算,零误差,性能好 -
慎用:double 构造 BigDecimal 或四舍五入补救
这类操作只是掩盖问题,初始数据已失真,审计不可复现
不只是 Java,所有语言都一样
Python 的 float、JavaScript 的 Number、C# 的 double、MySQL 的 FLOAT/DOUBLE——只要基于 IEEE 754 二进制标准,就逃不开这个限制。金融系统要求结果确定、可复现、零偏差,而 float 天生做不到。











