写点什么

BigDecimal 被拼多多的"砍一刀"应用到了极致

  • 2022 年 1 月 20 日
  • 本文字数:1518 字

    阅读完需:约 5 分钟

作者:幻好

来源:恒生LIGHT云社区


概述


近日,“拼多多砍价,砍价永远差一刀”登上了热搜,而这个功能在我们周围的亲朋好友中都有过亲身体验。
本次事件具体就去年3月,上海律师刘宇航当时参加了拼多多的“砍价免费拿”活动,领取了一张“超级免单卡”,但邀请多人砍价后,始终差“0.9%”。刘宇航以拼多多在提供网络服务时涉嫌违背诚实信用原则,使用虚假数据隐瞒规则已构成欺诈为由,向法院递交了起诉材料。在案件审理中,针对刘宇航的起诉以及由此引发的质疑,拼多多表示,因页面显示百分比位数有限,所以他们把一个至少小数点后有6位数以上的百分比,省略显示为0.9%,砍价页面显示的0.9%不是0.9%,而是0.9996427%。
复制代码



本文主要通过这一现实场景,来思考其背后相关的技术实现——Java 中精确小数计算。BigDecimal 应用背景


在 Java 中 JDK 中提供的 float 和 double 的数据类型,其主要设计目标是为了科学计算和工程计算,由于其执行的二进制浮点运算,在某些场景下并不能提供精确的结果。比如:


System.out.println(0.1 + 0.2);
// 输出0. 30000000000000004
复制代码

一个简单的加法运算,在输出的结果上并不能保证结果的准确性。其主要原因是在 CPU 中,表示的浮点数由两个部分组成:指数和尾数,这样一般都会导致失去一定的精确度,有些浮点数运算也会产生一定的误差,浮点数的值实际上是由一个特定的数学公式计算得到的。如:2.4 的二进制表示并非就是精确的 1.5。反而最为接近的二进制表示是 1.59999999999999。


所以在许多需要精确小数计算的商业场景(如支付,商城等),通常需要采用 java.math.BigDecimal 类来进行精确计算。操作使用创建 BigDecimal 对象

BigDecimal bigDecimal = new BigDecimal("2022.01054");
复制代码


通常建议使用 String 类型的构造方法,创建 BigDecimal 对象,虽然也可以使用 Double 类型的构造方法,但是前面就说过,浮点数本来就不准确,因此转换的 BigDecimal 对象也会不精确。
复制代码


格式化小数

// 向零方向舍入System.out.println(bigDecimal.setScale(3, BigDecimal.ROUND_DOWN));
// 输出2022.010
// 向远离0的方向舍入,进位处理new BigDecimal(4.32579).setScale(4, BigDecimal.ROUND_UP);
// 输出2022.011
// 向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入: >=5new BigDecimal(4.32575).setScale(4, BigDecimal.ROUND_HALF_UP);
// 输出2022.011
// 向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入: >5new BigDecimal(4.32575).setScale(4, BigDecimal.ROUND_HALF_DOWN);
// 输出2022.010
复制代码


加减乘除运算

// 推荐用字符串的形式初始化BigDecimal numFri = new BigDecimal("0.0888");BigDecimal numSec = new BigDecimal("0.2222");

// 加法BigDecimal resultByAdd = numFri.add(numSec);
// 减法BigDecimal resultBySub = numFri.subtract(numSec);
// 乘法BigDecimal resultByMul = numFri.multiply(numSec);
// 除法BigDecimal resultBydiv = numFri.divide(numSec,20,BigDecimal.ROUND_HALF_UP);
// 绝对值BigDecimal resultByAbs = numFri.abs();


复制代码


BigDecimal 对象在减乘除时,最终都返回的是一个新的 BigDecimal 对象,因为 BigInteger 与 BigDecimal 都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象
复制代码


总结


在需要精确计算的重要场景,使用 BigDecimal,同时推荐使用 String 类型的构造函数创建 BigDecimal 对象。


想向技术大佬们多多取经?开发中遇到的问题何处探讨?如何获取金融科技海量资源?


恒生 LIGHT 云社区,由恒生电子搭建的金融科技专业社区平台,分享实用技术干货、资源数据、金融科技行业趋势,拥抱所有金融开发者。


扫描下方小程序二维码,加入我们!


用户头像

还未添加个人签名 2018.11.07 加入

还未添加个人简介

评论

发布
暂无评论
BigDecimal 被拼多多的"砍一刀"应用到了极致