• 金融系统中正确的金额计算及存储方式


    昨天微信群里在讨论金额计算及存储的话题,今天特来结贴一下。

    经典的精度丢失问题

    Java中的类型float、double用来做计算会有精度丢失问题,下面来看下面的示例。

    public static void main(String[] args) {
        test1();
        test2();
    }
    
    private static void test1() {
        double totalAmount = 0.09;
        double feeAmount = 0.02;
        double tradeAmount = totalAmount - feeAmount;
        System.out.println(tradeAmount);
    }

    上面的程序输出结果是多少?

    0.07?非也!

    正确的结果是:

    0.06999999999999999

    为什么是这样?

    浮点数可能丢失精度,浮点十进制数通常没有完全相同的二进制的表示形式,这是CPU所采用的浮点数据表示形式的副作用。为此,可能会有一些精度丢失,并且一些浮点运算可能会产生未知的结果。

    浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。所以,在使用float、double作精确运算的时候一定要特别小心,除非能容忍精度丢失,不然产生的误差也是会造成双方对账不一致的结果。

    怎么解决

    在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal。

    BigDecimal适合更精度的运算,也提供了丰富的操作符类型,小数位控制,四舍五入规则等。

    不过,使用BigDecimal不当也有精度丢失的情况,如double的构造方法:

    BigDecimal(double val)

    再来看这个示例:

    private static void test2() {
        double totalAmount = 0.09;
        double feeAmount = 0.02;
        BigDecimal tradeAmount = new BigDecimal(totalAmount).subtract(new BigDecimal(feeAmount));
        System.out.println(tradeAmount);
    }

    输出:

    0.0699999999999999962529972918900966760702431201934814453125

    这个精度就更恐怖了。。

    所以,一定要使用String的构造方法:

    BigDecimal(String val)
    private static void test3() {
        double totalAmount = 0.09;
        double feeAmount = 0.02;
        BigDecimal tradeAmount = new BigDecimal(String.valueOf(totalAmount))
                .subtract(new BigDecimal(String.valueOf(feeAmount)));
        System.out.println(tradeAmount);
    }

    总结

    1. 金额运算尽量使用BigDecimal(String val)进行运算。

    2. 数据库存储金额,一般有整型和浮点型两种存储方式。如果是有汇率转换的,建议使用浮点数decimal进行存储,可以灵活的控制精度,decimal直接对应java类型BigDecimal。当然,用整数存储分这种形式也可以,转账的时候单位为元而如果忘了转换分为元,那就悲剧了。

    推荐阅读

    干货:免费领取2TB架构师四阶段视频教程

    面经:史上最全Java多线程面试题及答案

    工具:推荐一款在线创作流程图、思维导图软件

    分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。

  • 相关阅读:
    带你了解 MySQL Binlog 不为人知的秘密
    算法的空间复杂度
    算法的时间复杂度
    xargs--冬天里的一丝暖意
    "三剑客"之awk心中无剑
    hadoop之yarn(优化篇)
    对hadoop RPC的理解
    mysql优化篇(基于索引)
    [python学习手册-笔记]003.数值类型
    [python学习手册-笔记]001.python前言
  • 原文地址:https://www.cnblogs.com/java-stack/p/11952607.html
Copyright © 2020-2023  润新知