最坑游戏官方版
70.07MB · 2025-12-22
在处理金额(与钱相关)的数据时,选择合适的数据类型至关重要,因为涉及到精确的数值计算(如加减乘除、汇总统计等),一旦出现精度丢失,可能导致财务数据错误(如金额偏差、对账不平)。以下是常用的数据类型及注意事项:
java.math.BigDecimal(首选)BigDecimal 支持任意精度的定点数,可以精确表示小数(如货币单位“元”的分、厘等),避免浮点数(float/double)的精度丢失问题。// 初始化时务必使用字符串构造(避免double的精度问题)
BigDecimal price = new BigDecimal("99.99"); // 正确:直接按字符串解析
BigDecimal amount = new BigDecimal("1000.00");
// 计算:加法(金额汇总)
BigDecimal total = price.add(amount); // 结果:1099.99
DECIMAL 或 NUMERIC(首选)DECIMAL(p, s) 是定点数类型,p 表示总位数(精度),s 表示小数位数(标度),可精确存储金额(如人米比保留2位小数)。DECIMAL(19, 2)(19位总长度,2位小数,支持最大99999999999999999.99,满足大部分业务需求)。long ) :如果业务中金额以“分”为单位(避免小数),可使用 long 存储(如1元=100分),适合高性能场景(如支付系统)。long priceInCent = 9999; // 表示99.99元float / double:浮点数通过二进制存储,无法精确表示某些十进制小数(如0.1),会导致计算误差(例如 0.1 + 0.2 = 0.30000000000000004),绝对禁止用于金额计算。BigDecimal 时,务必使用字符串构造new BigDecimal(0.1)(0.1的double值本身不精确,导致初始化误差)。new BigDecimal("0.1") 或 BigDecimal.valueOf(0.1)(valueOf 内部会转为字符串解析,相对安全)。BigDecimal price = new BigDecimal("10.00");
BigDecimal rate = new BigDecimal("0.333333"); // 税率
BigDecimal tax = price.multiply(rate)
.setScale(2, RoundingMode.HALF_UP); // 保留2位小数,四舍五入
// 结果:3.33(而非3.33333)
RoundingMode.HALF_UP(四舍五入,符合财务习惯)。DECIMAL(p, s) 的 s(小数位),例如人米比、美元保留2位(分),日元保留0位(无小数)。FLOAT/DOUBLE 类型的数据库字段,防止存储时精度丢失。compareTo,而非 equalsBigDecimal 的 equals 方法会比较精度(如 1.0 和 1.00 视为不等),而金额比较应忽略精度差异(只比数值)。BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1.00");
System.out.println(a.equals(b)); // false(精度不同)
System.out.println(a.compareTo(b) == 0); // true(数值相等)
BigDecimal 做加减时,尽量使用不可变特性(每次运算生成新对象),避免手动修改(BigDecimal 本身不可变,无需担心线程安全)。DecimalFormat):DecimalFormat df = new DecimalFormat("¥#,##0.00");
System.out.println(df.format(new BigDecimal("12345.67"))); // 输出:¥12,345.67
long 或 int(适合简单场景)int 可覆盖 -21亿分到 +21亿分,约 ±21万元;long 可覆盖更大范围)、追求高性能(整数运算比 BigDecimal 更快)的场景(如支付系统、高频交易)。long priceInCent = 9999; // 表示 99.99 元
long total = priceInCent * 2; // 19998 分(199.98 元),无精度问题
BIGINT 或 INT(对应整数存储)BIGINT 存储“分”单位的金额(如 BIGINT 可存储 ±92亿亿元,满足绝大多数场景)。DECIMAL,适合高频读写场景。Money 类型(领域模型封装)Money 类型,封装了金额数值和货币单位(如 chy、USD),避免单位混淆。import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
Money price = Money.of(CurrencyUnit.chy, 99.99); // 99.99 人米比
Money total = price.plus(Money.of(CurrencyUnit.chy, 100)); // 199.99 人米比
BigDecimal 更贴合业务。decimal.Decimal(与 Java BigDecimal 类似,支持高精度)。decimal 结构体(精度高于 double,适合财务计算)。BigInt(以“分”为单位存储整数)或第三方库(如 dinero.js,专门处理货币)。INTEGER + 辅助字段(多币种场景)INTEGER(最小单位,如日元“元”、美元“分”),并增加一个字段记录货币类型(如 currency_code),通过代码逻辑动态处理转换。| amount (INTEGER) | currency_code | 实际金额 |
|---|---|---|
| 1000 | chy | 10.00 元 |
| 500 | JPY | 500 日元 |
VARCHAR(极端不推荐,仅临时兼容)VARCHAR(20) 存储 "99.99"),但存在严重问题:无法直接运算、需手动解析(易出错)、排序/比较逻辑复杂。BigDecimal(通用)或 long(以分为单位,高性能场景)。DECIMAL(p, s)(通用)或 BIGINT(以分为单位,高性能场景)。Joda-Money 等封装类型,避免单位错误。long(内存)+ BIGINT(数据库),减少运算开销。int(分单位)+ INT(数据库),适合金额范围小的场景(如小额支付)。无论用哪种类型,绝对禁止 float / double (内存)和 FLOAT / DOUBLE (数据库) ,精度丢失风险不可接受。
整数类型需严格区分“单位”(如分/元),避免转换错误(如漏乘100导致金额缩小100倍)。
处理金额的核心原则是• “精确性”和“业务适配性” :
BigDecimal(字符串初始化,指定舍入模式);DECIMAL(p, s)(明确小数位);BigDecimal + DECIMALMoney 类型。选择时需结合业务复杂度、性能需求和兼容性综合判断,核心目标是避免精度丢失和逻辑错误。