必须使用 bigdecimal 而非 float/double 处理金额,因后者用二进制存储十进制小数会导致无限循环、截断误差、计算放大及不可复现结果,金融场景要求零偏差。

因为 float 和 double 用二进制存储十进制小数,而像 0.1、0.05、19.99 这类常见金额,在二进制里全是无限循环小数,根本存不准——这不是 Java 的 bug,是数学和计算机底层表示方式决定的必然结果。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
十进制小数在二进制中天然无法精确表达
比如 0.1 元: 它在二进制中是 0.0001100110011…₂,无限循环,永远除不尽。 float(32 位)和 double(64 位)只能截断尾数,一创建就带初始误差。 你写 double x = 0.1;,内存里存的已经不是 0.1,而是最接近它的近似值。
误差会在加减乘除中快速放大
- 0.1 + 0.2 得到 0.30000000000000004,不等于 0.3
- 1.00 − 9 × 0.10 结果是 0.09999999999999998,少算 0.00000000000000002 元
- 循环扣减(如分次退款、分期还款)会让误差逐轮叠加,最终余额可能变成 0.3999999999999999 而不是应得的 0.40
所谓“精度”不等于“小数点后几位安全”
float 约 7 位有效数字,double 约 16 位——但这是整个数字的有效位数,不是小数部分:
- 金额如 12345678.9 已逼近 float 整数精度上限(±16777216),再做小数运算极易失真
- 即便用 double,12.35 在内存中可能实际是 12.34444444444444449,转成 BigDecimal 后四舍五入反而出错
- 数据库(如 MySQL 的 FLOAT/DOUBLE)同样存在存取不一致:插入 2.888888 可能自动存为 2.889
金融场景容不得“几乎正确”
银行账户、支付对账、发票开票、分润结算等所有环节,都要求结果确定、可复现、零偏差:
- 单笔误差仅 0.00000001 元,百万级交易后可能差出几百元
- 系统间比对时,一个 == 判断就会失败(如 0.1f + 0.2f != 0.3f)
- 这不是代码写得不好,而是 float/double 的设计目标本就不是精确十进制运算
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










