精确金额计算完美方案:让每一分钱都算得清清楚楚!💰

75 阅读9分钟

标题: 金额计算还在用Double?BigDecimal才是王道!
副标题: 从精度丢失到分为单位,彻底解决金额计算问题


🎬 开篇:一次致命的精度丢失

某电商平台优惠券系统:

用户下单:100元
优惠券:8.8折
应付金额:100 * 0.88 = 88.0元

代码实现(错误):
double price = 100.0;
double discount = 0.88;
double finalPrice = price * discount;
// 结果:87.99999999999999 💀

用户支付:87.99元
实际应付:88.00元
差额:0.01元

结果:
- 10万订单,累计差额1000元
- 财务对账不平
- 用户投诉
- 公司损失 💸

老板:为什么会这样?!
开发:我用了double... 😭

改用BigDecimal后:
- 精度完全准确
- 对账零差异
- 老板放心了 😊

教训:金额计算绝对不能用float/double!

🤔 为什么不能用Float/Double?

public class FloatProblem {
    public static void main(String[] args) {
        // ❌ 错误示范
        System.out.println(0.1 + 0.2);  // 0.30000000000000004
        System.out.println(1.0 - 0.9);  // 0.09999999999999998
        System.out.println(4.015 * 100); // 401.49999999999994
        
        // 商业计算的灾难
        double price = 2.0;
        double discount = 0.1;
        double finalPrice = price - discount;
        System.out.println(finalPrice);  // 1.9000000000000001 💀
    }
}

原因: Float/Double使用IEEE 754标准,二进制无法精确表示某些十进制小数!


📚 知识地图

金额计算解决方案
├── ✅ 正确方案
│   ├── BigDecimal(推荐)⭐⭐⭐⭐⭐
│   ├── 分为单位(整数运算)⭐⭐⭐⭐⭐
│   └── 第三方库(Joda-Money)⭐⭐⭐
├── ⚡ 核心要点
│   ├── 精度控制
│   ├── 舍入模式
│   ├── 比较判断
│   └── 格式化输出
└── 🎯 最佳实践
    ├── 数据库存储(DECIMAL)
    ├── 接口传输(字符串)
    ├── 计算处理(BigDecimal)
    └── 日志记录(格式化)

✅ 方案1:BigDecimal(推荐)

基础使用

/**
 * BigDecimal基础用法
 */
public class BigDecimalBasic {
    
    public static void main(String[] args) {
        // ✅ 正确的创建方式
        BigDecimal price1 = new BigDecimal("100.00");  // 字符串(推荐)
        BigDecimal price2 = BigDecimal.valueOf(100.00); // valueOf方法
        
        // ❌ 错误的创建方式
        BigDecimal price3 = new BigDecimal(0.1);  // double构造(精度丢失!)
        System.out.println(price3);  // 0.1000000000000000055511151231257827021181583404541015625
        
        // ✅ 正确示例
        BigDecimal correct = new BigDecimal("0.1");
        System.out.println(correct);  // 0.1
        
        // 基本运算
        BigDecimal a = new BigDecimal("100.00");
        BigDecimal b = new BigDecimal("88.00");
        
        System.out.println("加法:" + a.add(b));        // 188.00
        System.out.println("减法:" + a.subtract(b));   // 12.00
        System.out.println("乘法:" + a.multiply(b));   // 8800.0000
        System.out.println("除法:" + a.divide(b, 2, RoundingMode.HALF_UP));  // 1.14
    }
}

核心工具类

/**
 * 金额计算工具类
 */
public class MoneyUtils {
    
    /**
     * 默认精度(小数位数)
     */
    private static final int DEFAULT_SCALE = 2;
    
    /**
     * 默认舍入模式(四舍五入)
     */
    private static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_UP;
    
    /**
     * 加法
     */
    public static BigDecimal add(BigDecimal a, BigDecimal b) {
        if (a == null) {
            a = BigDecimal.ZERO;
        }
        if (b == null) {
            b = BigDecimal.ZERO;
        }
        return a.add(b);
    }
    
    /**
     * 减法
     */
    public static BigDecimal subtract(BigDecimal a, BigDecimal b) {
        if (a == null) {
            a = BigDecimal.ZERO;
        }
        if (b == null) {
            b = BigDecimal.ZERO;
        }
        return a.subtract(b);
    }
    
    /**
     * 乘法(自动保留2位小数)
     */
    public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
        if (a == null || b == null) {
            return BigDecimal.ZERO;
        }
        return a.multiply(b).setScale(DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
    }
    
    /**
     * 除法(自动保留2位小数)
     */
    public static BigDecimal divide(BigDecimal a, BigDecimal b) {
        if (a == null || b == null || b.compareTo(BigDecimal.ZERO) == 0) {
            throw new IllegalArgumentException("除数不能为null或0");
        }
        return a.divide(b, DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
    }
    
    /**
     * 除法(自定义精度)
     */
    public static BigDecimal divide(BigDecimal a, BigDecimal b, int scale) {
        if (a == null || b == null || b.compareTo(BigDecimal.ZERO) == 0) {
            throw new IllegalArgumentException("除数不能为null或0");
        }
        return a.divide(b, scale, DEFAULT_ROUNDING_MODE);
    }
    
    /**
     * 比较大小
     * @return a > b 返回1,a == b 返回0,a < b 返回-1
     */
    public static int compare(BigDecimal a, BigDecimal b) {
        if (a == null) {
            a = BigDecimal.ZERO;
        }
        if (b == null) {
            b = BigDecimal.ZERO;
        }
        return a.compareTo(b);
    }
    
    /**
     * 是否相等(推荐用compareTo,不要用equals)
     */
    public static boolean equals(BigDecimal a, BigDecimal b) {
        if (a == null) {
            a = BigDecimal.ZERO;
        }
        if (b == null) {
            b = BigDecimal.ZERO;
        }
        // ⚡ 使用compareTo比较,忽略精度差异
        // new BigDecimal("1.0").equals(new BigDecimal("1.00")) = false
        // new BigDecimal("1.0").compareTo(new BigDecimal("1.00")) = 0
        return a.compareTo(b) == 0;
    }
    
    /**
     * 是否大于
     */
    public static boolean greaterThan(BigDecimal a, BigDecimal b) {
        return compare(a, b) > 0;
    }
    
    /**
     * 是否大于等于
     */
    public static boolean greaterThanOrEqual(BigDecimal a, BigDecimal b) {
        return compare(a, b) >= 0;
    }
    
    /**
     * 是否小于
     */
    public static boolean lessThan(BigDecimal a, BigDecimal b) {
        return compare(a, b) < 0;
    }
    
    /**
     * 是否小于等于
     */
    public static boolean lessThanOrEqual(BigDecimal a, BigDecimal b) {
        return compare(a, b) <= 0;
    }
    
    /**
     * 格式化金额(保留2位小数)
     */
    public static String format(BigDecimal amount) {
        if (amount == null) {
            amount = BigDecimal.ZERO;
        }
        DecimalFormat df = new DecimalFormat("#,##0.00");
        return df.format(amount);
    }
    
    /**
     * 格式化金额(带货币符号)
     */
    public static String formatWithSymbol(BigDecimal amount) {
        if (amount == null) {
            amount = BigDecimal.ZERO;
        }
        DecimalFormat df = new DecimalFormat("¥#,##0.00");
        return df.format(amount);
    }
    
    /**
     * 字符串转BigDecimal
     */
    public static BigDecimal parse(String amount) {
        if (StringUtils.isBlank(amount)) {
            return BigDecimal.ZERO;
        }
        
        try {
            // 去除千分位逗号和货币符号
            amount = amount.replaceAll("[,¥$€£]", "").trim();
            return new BigDecimal(amount);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("金额格式错误:" + amount, e);
        }
    }
    
    /**
     * 保留指定小数位数
     */
    public static BigDecimal setScale(BigDecimal amount, int scale) {
        if (amount == null) {
            return BigDecimal.ZERO;
        }
        return amount.setScale(scale, DEFAULT_ROUNDING_MODE);
    }
    
    /**
     * 保留2位小数(默认)
     */
    public static BigDecimal setScale(BigDecimal amount) {
        return setScale(amount, DEFAULT_SCALE);
    }
    
    /**
     * 百分比计算
     * 例:100 * 88% = 88.00
     */
    public static BigDecimal percentage(BigDecimal amount, BigDecimal percent) {
        if (amount == null || percent == null) {
            return BigDecimal.ZERO;
        }
        
        // 将百分比转换为小数(88% -> 0.88)
        BigDecimal rate = divide(percent, new BigDecimal("100"));
        
        return multiply(amount, rate);
    }
    
    /**
     * 折扣计算
     * 例:100 * 8.8折 = 88.00
     */
    public static BigDecimal discount(BigDecimal amount, BigDecimal discount) {
        if (amount == null || discount == null) {
            return BigDecimal.ZERO;
        }
        
        // 将折扣转换为小数(8.8折 -> 0.88)
        BigDecimal rate = divide(discount, BigDecimal.TEN);
        
        return multiply(amount, rate);
    }
    
    /**
     * 最小值
     */
    public static BigDecimal min(BigDecimal a, BigDecimal b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return a.compareTo(b) < 0 ? a : b;
    }
    
    /**
     * 最大值
     */
    public static BigDecimal max(BigDecimal a, BigDecimal b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return a.compareTo(b) > 0 ? a : b;
    }
    
    /**
     * 求和
     */
    public static BigDecimal sum(BigDecimal... amounts) {
        if (amounts == null || amounts.length == 0) {
            return BigDecimal.ZERO;
        }
        
        BigDecimal total = BigDecimal.ZERO;
        for (BigDecimal amount : amounts) {
            if (amount != null) {
                total = total.add(amount);
            }
        }
        
        return total;
    }
    
    /**
     * 求和(集合)
     */
    public static BigDecimal sum(List<BigDecimal> amounts) {
        if (amounts == null || amounts.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        return amounts.stream()
            .filter(Objects::nonNull)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

🎯 舍入模式详解

/**
 * 舍入模式示例
 */
public class RoundingModeExample {
    
    public static void main(String[] args) {
        BigDecimal value = new BigDecimal("2.355");
        
        // HALF_UP:四舍五入(最常用)⭐⭐⭐⭐⭐
        System.out.println("HALF_UP: " + 
            value.setScale(2, RoundingMode.HALF_UP));  // 2.36
        
        // HALF_DOWN:五舍六入
        System.out.println("HALF_DOWN: " + 
            value.setScale(2, RoundingMode.HALF_DOWN));  // 2.35
        
        // HALF_EVEN:银行家舍入法(四舍六入五取偶)
        BigDecimal v1 = new BigDecimal("2.355");
        BigDecimal v2 = new BigDecimal("2.365");
        System.out.println("HALF_EVEN (2.355): " + 
            v1.setScale(2, RoundingMode.HALF_EVEN));  // 2.36(5前面是5,向上)
        System.out.println("HALF_EVEN (2.365): " + 
            v2.setScale(2, RoundingMode.HALF_EVEN));  // 2.36(5前面是6,向上)
        
        // UP:向上取整(远离0)
        System.out.println("UP: " + 
            value.setScale(2, RoundingMode.UP));  // 2.36
        
        // DOWN:向下取整(靠近0,直接截断)
        System.out.println("DOWN: " + 
            value.setScale(2, RoundingMode.DOWN));  // 2.35
        
        // CEILING:向正无穷方向取整
        BigDecimal positive = new BigDecimal("2.351");
        BigDecimal negative = new BigDecimal("-2.351");
        System.out.println("CEILING (positive): " + 
            positive.setScale(2, RoundingMode.CEILING));  // 2.36
        System.out.println("CEILING (negative): " + 
            negative.setScale(2, RoundingMode.CEILING));  // -2.35
        
        // FLOOR:向负无穷方向取整
        System.out.println("FLOOR (positive): " + 
            positive.setScale(2, RoundingMode.FLOOR));  // 2.35
        System.out.println("FLOOR (negative): " + 
            negative.setScale(2, RoundingMode.FLOOR));  // -2.36
        
        // UNNECESSARY:不需要舍入(如果有精度丢失会抛异常)
        BigDecimal exact = new BigDecimal("2.35");
        System.out.println("UNNECESSARY: " + 
            exact.setScale(2, RoundingMode.UNNECESSARY));  // 2.35
        // 下面这行会抛异常:ArithmeticException
        // value.setScale(2, RoundingMode.UNNECESSARY);
    }
}

/**
 * 推荐使用场景:
 * 
 * 1. 一般商业计算:RoundingMode.HALF_UP(四舍五入)⭐⭐⭐⭐⭐
 * 2. 金融计算:RoundingMode.HALF_EVEN(银行家舍入)⭐⭐⭐⭐
 * 3. 库存扣减:RoundingMode.DOWN(向下取整,宁少勿多)⭐⭐⭐
 * 4. 税费计算:RoundingMode.UP(向上取整)⭐⭐⭐
 */

💾 方案2:分为单位(整数运算)

/**
 * 分为单位方案
 * 
 * 核心思想:将金额转换为分(最小单位),使用整数运算,避免浮点数精度问题
 */
public class CentCalculation {
    
    /**
     * 元转分
     */
    public static long yuan2cent(BigDecimal yuan) {
        if (yuan == null) {
            return 0L;
        }
        
        // 乘以100,转换为分
        return yuan.multiply(new BigDecimal("100"))
                   .setScale(0, RoundingMode.HALF_UP)
                   .longValue();
    }
    
    /**
     * 分转元
     */
    public static BigDecimal cent2yuan(long cent) {
        return new BigDecimal(cent)
                   .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
    }
    
    /**
     * 示例:订单金额计算
     */
    public static void main(String[] args) {
        // 商品价格:99.99元
        BigDecimal price = new BigDecimal("99.99");
        // 数量:3
        int quantity = 3;
        
        // 方式1:BigDecimal计算
        BigDecimal total1 = price.multiply(new BigDecimal(quantity))
                                 .setScale(2, RoundingMode.HALF_UP);
        System.out.println("BigDecimal计算:" + total1);  // 299.97
        
        // 方式2:分为单位计算
        long priceCent = yuan2cent(price);  // 9999分
        long totalCent = priceCent * quantity;  // 29997分
        BigDecimal total2 = cent2yuan(totalCent);  // 299.97元
        System.out.println("分为单位计算:" + total2);  // 299.97
    }
}

/**
 * 优点:
 * ✅ 性能好(整数运算比BigDecimal快)
 * ✅ 避免浮点数精度问题
 * ✅ 数据库可以用BIGINT存储
 * 
 * 缺点:
 * ⚠️ 需要来回转换
 * ⚠️ 代码可读性稍差
 * ⚠️ 不适合需要高精度的场景(如汇率计算)
 * 
 * 适用场景:
 * ✅ 高性能要求
 * ✅ 只需要精确到分
 * ✅ 电商订单、支付系统
 */

💾 数据库存储方案

-- ❌ 错误的字段类型
CREATE TABLE `order` (
    id BIGINT PRIMARY KEY,
    total_amount FLOAT,  -- ❌ 绝对不要用FLOAT/DOUBLE
    discount_amount DOUBLE  -- ❌ 精度丢失
);

-- ✅ 正确的字段类型
CREATE TABLE `order` (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    -- 方案1:DECIMAL(推荐)⭐⭐⭐⭐⭐
    total_amount DECIMAL(10, 2) NOT NULL COMMENT '订单总金额(元)',
    discount_amount DECIMAL(10, 2) NOT NULL DEFAULT 0 COMMENT '优惠金额(元)',
    freight_amount DECIMAL(10, 2) NOT NULL DEFAULT 0 COMMENT '运费(元)',
    actual_amount DECIMAL(10, 2) NOT NULL COMMENT '实付金额(元)',
    
    -- 方案2:BIGINT(分为单位)⭐⭐⭐⭐
    total_amount_cent BIGINT NOT NULL COMMENT '订单总金额(分)',
    discount_amount_cent BIGINT NOT NULL DEFAULT 0 COMMENT '优惠金额(分)',
    
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

/**
 * DECIMAL类型说明:
 * DECIMAL(M, D)
 * - M:总位数(整数位+小数位),最大65
 * - D:小数位数,最大30
 * 
 * 示例:
 * DECIMAL(10, 2):最大值 99999999.99(8位整数,2位小数)
 * DECIMAL(15, 2):最大值 9999999999999.99(13位整数,2位小数)
 * 
 * 推荐:
 * - 普通金额:DECIMAL(10, 2)
 * - 大额金额:DECIMAL(15, 2)
 * - 汇率等:DECIMAL(10, 6)
 */

🎯 实战案例:订单金额计算

/**
 * 订单金额计算服务
 */
@Service
@Slf4j
public class OrderAmountService {
    
    /**
     * 计算订单金额
     */
    public OrderAmountDTO calculateOrderAmount(OrderCalculateDTO dto) {
        OrderAmountDTO result = new OrderAmountDTO();
        
        // 1. 计算商品总金额
        BigDecimal goodsAmount = calculateGoodsAmount(dto.getItems());
        result.setGoodsAmount(goodsAmount);
        
        // 2. 计算运费
        BigDecimal freightAmount = calculateFreight(dto);
        result.setFreightAmount(freightAmount);
        
        // 3. 计算优惠金额
        BigDecimal discountAmount = calculateDiscount(dto, goodsAmount);
        result.setDiscountAmount(discountAmount);
        
        // 4. ⚡ 计算实付金额(商品总额 + 运费 - 优惠)
        BigDecimal actualAmount = goodsAmount
            .add(freightAmount)
            .subtract(discountAmount);
        
        // 确保实付金额不为负数
        if (MoneyUtils.lessThan(actualAmount, BigDecimal.ZERO)) {
            actualAmount = BigDecimal.ZERO;
        }
        
        // 保留2位小数
        actualAmount = MoneyUtils.setScale(actualAmount);
        result.setActualAmount(actualAmount);
        
        log.info("订单金额计算完成:商品{}元 + 运费{}元 - 优惠{}元 = 实付{}元",
            goodsAmount, freightAmount, discountAmount, actualAmount);
        
        return result;
    }
    
    /**
     * 计算商品总金额
     */
    private BigDecimal calculateGoodsAmount(List<OrderItemDTO> items) {
        if (items == null || items.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        return items.stream()
            .map(item -> {
                BigDecimal price = item.getPrice();
                BigDecimal quantity = new BigDecimal(item.getQuantity());
                
                // 单品金额 = 单价 * 数量
                return MoneyUtils.multiply(price, quantity);
            })
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    /**
     * 计算运费
     */
    private BigDecimal calculateFreight(OrderCalculateDTO dto) {
        BigDecimal freightAmount = dto.getFreightAmount();
        
        if (freightAmount == null) {
            freightAmount = BigDecimal.ZERO;
        }
        
        // 满99包邮
        BigDecimal goodsAmount = calculateGoodsAmount(dto.getItems());
        BigDecimal freeFreightThreshold = new BigDecimal("99.00");
        
        if (MoneyUtils.greaterThanOrEqual(goodsAmount, freeFreightThreshold)) {
            freightAmount = BigDecimal.ZERO;
        }
        
        return freightAmount;
    }
    
    /**
     * 计算优惠金额
     */
    private BigDecimal calculateDiscount(OrderCalculateDTO dto, BigDecimal goodsAmount) {
        BigDecimal totalDiscount = BigDecimal.ZERO;
        
        // 1. 优惠券优惠
        if (dto.getCouponId() != null) {
            BigDecimal couponDiscount = calculateCouponDiscount(dto.getCouponId(), goodsAmount);
            totalDiscount = totalDiscount.add(couponDiscount);
        }
        
        // 2. 满减优惠
        BigDecimal fullReductionDiscount = calculateFullReduction(goodsAmount);
        totalDiscount = totalDiscount.add(fullReductionDiscount);
        
        // 3. 确保优惠金额不超过商品总额
        if (MoneyUtils.greaterThan(totalDiscount, goodsAmount)) {
            totalDiscount = goodsAmount;
        }
        
        return MoneyUtils.setScale(totalDiscount);
    }
    
    /**
     * 计算优惠券优惠
     */
    private BigDecimal calculateCouponDiscount(Long couponId, BigDecimal goodsAmount) {
        // TODO: 查询优惠券信息
        
        // 示例:8.8折优惠券
        BigDecimal discount = new BigDecimal("8.8");
        
        // 折后金额
        BigDecimal discountAmount = MoneyUtils.discount(goodsAmount, discount);
        
        // 优惠金额 = 原价 - 折后价
        return MoneyUtils.subtract(goodsAmount, discountAmount);
    }
    
    /**
     * 计算满减优惠
     */
    private BigDecimal calculateFullReduction(BigDecimal goodsAmount) {
        // 满200减30
        BigDecimal threshold = new BigDecimal("200.00");
        BigDecimal reduction = new BigDecimal("30.00");
        
        if (MoneyUtils.greaterThanOrEqual(goodsAmount, threshold)) {
            return reduction;
        }
        
        return BigDecimal.ZERO;
    }
}

/**
 * 订单金额DTO
 */
@Data
public class OrderAmountDTO {
    
    /**
     * 商品总金额
     */
    private BigDecimal goodsAmount;
    
    /**
     * 运费
     */
    private BigDecimal freightAmount;
    
    /**
     * 优惠金额
     */
    private BigDecimal discountAmount;
    
    /**
     * 实付金额
     */
    private BigDecimal actualAmount;
}

⚡ JSON序列化处理

/**
 * BigDecimal的JSON序列化配置
 */
@Configuration
public class JacksonConfig {
    
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> {
            // ⚡ BigDecimal序列化为字符串(避免精度丢失)
            builder.serializerByType(BigDecimal.class, new JsonSerializer<BigDecimal>() {
                @Override
                public void serialize(BigDecimal value, JsonGenerator gen, 
                                    SerializerProvider serializers) throws IOException {
                    if (value == null) {
                        gen.writeNull();
                    } else {
                        // 保留2位小数,并转为字符串
                        gen.writeString(value.setScale(2, RoundingMode.HALF_UP).toString());
                    }
                }
            });
            
            // ⚡ 字符串反序列化为BigDecimal
            builder.deserializerByType(BigDecimal.class, new JsonDeserializer<BigDecimal>() {
                @Override
                public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) 
                    throws IOException {
                    String value = p.getText();
                    if (StringUtils.isBlank(value)) {
                        return BigDecimal.ZERO;
                    }
                    return new BigDecimal(value);
                }
            });
        };
    }
}

/**
 * 或者在字段上使用注解
 */
@Data
public class OrderVO {
    
    private Long id;
    
    /**
     * ⚡ 使用@JsonSerialize注解
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private BigDecimal totalAmount;
    
    /**
     * ⚡ 或者自定义格式化
     */
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#0.00")
    private BigDecimal actualAmount;
}

✅ 最佳实践

金额计算黄金法则:

1️⃣ 数据类型:
   □ Java:BigDecimal(禁止Float/Double)
   □ 数据库:DECIMAL(M, D)
   □ 接口传输:String(JSON)
   
2️⃣ 创建BigDecimal:
   □ ✅ new BigDecimal("100.00")  // 字符串
   □ ✅ BigDecimal.valueOf(100.00)
   □ ❌ new BigDecimal(0.1)  // double(精度丢失)
   
3️⃣ 舍入模式:
   □ 一般计算:RoundingMode.HALF_UP(四舍五入)
   □ 金融计算:RoundingMode.HALF_EVEN(银行家舍入)
   □ 库存扣减:RoundingMode.DOWN(向下取整)
   
4️⃣ 比较判断:
   □ ✅ amount.compareTo(other) == 0  // 相等
   □ ❌ amount.equals(other)  // 不推荐(精度差异)
   
5️⃣ 格式化输出:
   □ 保留2位小数
   □ 千分位分隔
   □ 货币符号
   
6️⃣ 性能优化:
   □ 复用BigDecimal对象
   □ 使用分为单位(高性能场景)
   □ 缓存常用值(0、1、100等)
   
7️⃣ 安全防护:
   □ 非空校验
   □ 精度控制
   □ 范围校验
   □ 日志记录

🎉 总结

金额计算核心要点:

1️⃣ 禁止使用Float/Double
2️⃣ 使用BigDecimal或分为单位
3️⃣ 注意舍入模式(HALF_UP)
4️⃣ 用compareTo而不是equals
5️⃣ 数据库用DECIMAL类型
6️⃣ JSON传输用字符串
7️⃣ 充分测试边界情况

记住:金额计算无小事,一分一厘都要精准! 💰


文档编写时间:2025年10月24日
作者:热爱精确计算的金融工程师
版本:v1.0
愿每一笔账都清清楚楚!