专栏名称: 码小辫
给程序员和编程爱好者分享计算机编程电子书以及相关的学习资源
目录
相关文章推荐
传媒招聘那些事儿  ·  “职”等你来!南方日报深圳区域公司招聘 ·  11 小时前  
传媒招聘那些事儿  ·  小红书:公关价值传播 ·  昨天  
传媒招聘那些事儿  ·  【职业咨询】1V1模拟面试/语音答疑服务助力求职! ·  昨天  
51好读  ›  专栏  ›  码小辫

2.01变成了2.00,金额使用 double 被坑了!

码小辫  · 公众号  ·  · 2025-03-09 17:10

正文


前些日子,测试提过来一个bug,说下单价格应该是 2.01,但是在订单详情中展示了2.00元。我头嗡的一下子,艹,不会是因为double 的精度问题吧~

果不其然,经过排查代码,最终定位原因订单详情展示金额时,使用double 进行了金额转换,导致金额不准。

我马上排查核心购买和售后链路,发现涉及资金交易的地方没有问题,只有这一处问题,要不然这一口大锅非得扣我身上。

为什么 2.01 变成了 2.0

2.01等小数 在计算机中按照2进制补码存储时,存在除不尽,精度丢失的问题。

例如  2.01的补码为 000000010.009999999999999787 。正如十进制场景存在 1/3等无限小数问题,二进制场景也存在无限小数,所以一定会存在精度问题。

什么场景小数转换存在问题

for (int money = 0; money < 10000; money++) {
   String valueYuan = String.format("%.2f", money * 1.0 / 100);

   int value = (int) (Double.valueOf(valueYuan) * 100);
   if (value != money) {
      System.out.println(String.format("原值: %s, 现值:%s", money, value));
   }
}

如上代码中,先将数字 除以 100,转为元, 精度为2位,然后将double 乘以100,转为int。在除以、乘以两个操作后,精度出现丢失。

我把1-10000 的范围测试一遍,共有573个数字出现精度转换错误。这个概率已经相当大了。

如何转换金额更安全?

Java 提供了 BigDecimcal 专门处理精度更高的浮点数。简单封装一下代码,元使用String表示,分使用int表示。提供两个方法实现 元和分的 互转。

public static String change2Yuan(int money) {
   BigDecimal base = BigDecimal.valueOf(money);
   BigDecimal yuanBase = base.divide(new BigDecimal(100));
   return yuanBase.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
}

public static int change2Fen(String money) {
   BigDecimal base = new BigDecimal(money);

   BigDecimal fenBase = base.multiply(new BigDecimal(100));
   return fenBase.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
}
测试






请到「今天看啥」查看全文