前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java避坑指南:涉及金钱计算时使用BigDecimal如何避坑

Java避坑指南:涉及金钱计算时使用BigDecimal如何避坑

作者头像
崔认知
发布2023-06-19 17:01:17
3600
发布2023-06-19 17:01:17
举报
文章被收录于专栏:nobody

简介


在涉及金钱交易交易计算时,为了避免浮点数计算对精度的影响,我们都会选择BigDecimal来处理。但是BigDecimal的有些方法是有坑的,稍不注意也会产生精度计算的问题。

BigDecimal坑一: 禁止使用java.math.BigDecimal#BigDecimal(double)构造函数方法


示例:

代码语言:javascript
复制
/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args)  {
        BigDecimal bigDecimal = new BigDecimal(1.019);
        System.out.println(bigDecimal);
    }
}

输出:

结果肯定不是我们想要的。我们看一下javadoc给的解释:

浮点数在计算机硬件中存储会丢失精度风险。为了避免这种情况,我们需要使用字符串参数的构造函数

代码语言:javascript
复制
java.math.BigDecimal#BigDecimal(java.lang.String)

示例:

代码语言:javascript
复制
/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args)  {
        BigDecimal bigDecimal = new BigDecimal("1.019");
        System.out.println(bigDecimal);
    }
}

输出:

BigDecimal坑二: 禁止使用java.math.BigDecimal#valueOf(double)静态方法


示例:

代码语言:javascript
复制
/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args)  {
        BigDecimal bigDecimal =   BigDecimal.valueOf(1.019111456677999999);
        System.out.println(bigDecimal);
    }
}

输出:

1.019111456677999999 输入,变成了1.019111456678。这也是由于我们输入的是double类型的精度问题导致。这和坑一情况一致。

BigDecimal坑三:禁止使用java.math.BigDecimal#equals比较


示例:

代码语言:javascript
复制
/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args)  {
        BigDecimal a =  new  BigDecimal("1.0");
        BigDecimal b = new BigDecimal("1");
        System.out.println(a.equals(b));
    }
}

输出:

我们看一下javadoc官方解释:

BigDecimal的equals方法会比较比较其“无标度值”和“标度值”,只有两者都相等才相等,而非我们业务逻辑即金钱关系的相等。为了避免这种情况的发生,我们使用compareTo方法即可。

BigDecimal坑四:禁止使用java.math.BigDecimal#round 进行舍入运算


示例:

代码语言:javascript
复制
/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args)  {
        BigDecimal x = new BigDecimal("123.56789");
        x = x.round(new MathContext(2, RoundingMode.HALF_UP));
        System.out.println(x);
        System.out.println("x=" + x.toPlainString());
        System.out.println("scale=" + x.scale());
    }
}

输出结果:

期望结果123.57,而结果真是意料之外。

因为此round方法只是从左到右进行舍入位数。

比如:123111.56789,从左到右舍入2位,小数部分,结果为:120000

代码语言:javascript
复制
/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args)  {
        BigDecimal x = new BigDecimal("123111.56789");
        x = x.round(new MathContext(2, RoundingMode.HALF_UP));
        System.out.println(x);
        System.out.println("x=" + x.toPlainString());
        System.out.println("scale=" + x.scale());
    }
}

进行舍入运算时,我们可以使用方法:

代码语言:javascript
复制
java.math.BigDecimal#setScale(int, java.math.RoundingMode)

示例:

代码语言:javascript
复制
/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args)  {
        BigDecimal x = new BigDecimal("1235.6789");
        x = x.setScale(2, RoundingMode.HALF_UP);
        System.out.println("x=" + x);
    }
}

输出:

小结


【🈲】禁止使用java.math.BigDecimal#BigDecimal(double)构造函数方法;

【🈲】禁止使用java.math.BigDecimal#valueOf(double)静态方法;

【🈲】禁止使用java.math.BigDecimal#equals比较;

【🈲】禁止使用java.math.BigDecimal#round 进行舍入运算;

【👍🏻】使用字符串参数的构造函数java.math.BigDecimal#BigDecimal(java.lang.String)构造BigDecimal,防止精度丢失;

【👍🏻】使用compareTo方法比较BigDecimal对象;

【👍🏻】使用java.math.BigDecimal#setScale(int, java.math.RoundingMode)进行舍入运算;

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 认知科技技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档