💰 金额字段设计与 BigDecimal 笔记
一、为什么不能用 float/double 表示金额?
-
float
/double
属于 二进制浮点数,底层无法精确表示大多数十进制小数。 -
示例:
System.out.println(0.1 + 0.2); // 输出:0.30000000000000004
-
会导致 金额误差,在金融、电商等系统中无法接受。
二、BigDecimal 是怎么解决精度问题的?
-
BigDecimal
使用 定点数表示法:-
unscaledValue
: 实际整数值 -
scale
: 小数位数
-
-
例如:
BigDecimal amount = new BigDecimal("123.45"); // 实际底层等价于 unscaledValue = 12345, scale = 2
✅ 这样就能精确表示任意小数,避免了浮点误差。
三、数据库中金额字段怎么选?
方案一:使用 DECIMAL(p, s)
类型(推荐)
-
精确表示小数,如
DECIMAL(18,2)
表示最多 18 位,其中 2 位为小数。 -
自动映射为 Java 的
BigDecimal
,无精度丢失。
✔ 优点:
-
精确、安全
-
代码和 SQL 表达一致,易读、易维护
-
支持 SQL 的金额聚合、计算函数
✘ 缺点:
-
存储空间稍大
-
极少数情况下性能略低于整型
方案二:金额转整数,用 BIGINT
存储(高性能方案)
-
将金额 *100(即“元转分”)存为整数,显示时再除以 100。
-
Java 使用
long
存储,如12345
表示123.45 元
✔ 优点:
-
存储空间小,查询快
-
可在极限高并发下优化性能
✘ 缺点:
-
不直观,容易混淆单位(分/厘/毫)
-
所有金额字段都需特殊转换
-
数据库聚合计算易错
四、Java 金额字段应该用什么类型?
类型 | 是否推荐 | 原因说明 |
---|---|---|
float / double | ❌ 不推荐 | 有精度误差 |
BigDecimal | ✅ 推荐 | 精确,适合金额计算 |
long | ✔ 特殊场景 | 可用于“金额转分”场景 |
五、最佳实践总结 ✅
场景 | 类型 | 说明 |
---|---|---|
数据库金额字段 | DECIMAL(18,2) | 业务直观、安全精确,主流做法 |
Java 金额变量 | BigDecimal | 避免浮点误差,直接与 DB 对应 |
极限性能、明确单位规范场景 | BIGINT | 金额转分存储,但需全局规范配套 |
🔚 结语
-
对于绝大多数应用,数据库用 DECIMAL,Java 用 BigDecimal 是首选方案。
-
使用整型模拟金额需要配套好单位约定、工具类支持,并且团队成员都理解。