问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

不是我说,不掌握这些坑,你敢用BigDecimal吗?

发布网友 发布时间:2024-10-05 22:13

我来回答

1个回答

热心网友 时间:2024-10-14 09:31

一直从事金融相关项目,对BigDecimal了如指掌,也见证了许多因为对其不了解或使用不当导致的资损事件。

如果你从事金融相关项目,或者你的项目中涉及到金额的计算,务必花时间阅读这篇文章,全面了解BigDecimal。

Java的java.math包提供了BigDecimal API类,用于对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或更小的数进行运算和处理。

一般情况下,对于不需要精确计算精度的数字,可以直接使用Float和Double处理,但Double.valueOf(String)和Float.valueOf(String)会丢失精度。因此,如果需要精确计算的结果,则必须使用BigDecimal类来操作。

BigDecimal对象提供了传统的+、-、*、/等算术运算符对应的方法,通过这些方法进行相应的操作。BigDecimal都是不可变的(immutable)的,在进行每一次四则运算时,都会产生一个新的对象,所以在做加减乘除运算时要记得保存操作后的值。

在使用BigDecimal时,有4种使用场景下的坑,你一定要了解一下,如果使用不当,必定很惨。掌握这些案例,当别人写出有坑的代码,你也能够一眼识别出来,大牛就是这么练成的。

第一:浮点类型的坑

在使用Float、Double等浮点类型进行计算时,有可能得到的是一个近似值,而不是精确的值。

比如下面的代码:结果是多少?0.1吗?不是,执行上面代码执行的结果是0.100000024。之所以产生这样的结果,是因为0.1的二进制表示是无限循环的。由于计算机的资源是有限的,所以是没办法用二进制精确地表示0.1,只能用「近似值」来表示,就是在有限的精度情况下,最大化接近0.1的二进制数,于是就会造成精度缺失的情况。

关于上述的现象大家都知道,不再详细展开。同时,还会得出结论在科学计数法时可考虑使用浮点类型,但如果是涉及到金额计算要使用BigDecimal来计算。

那么,BigDecimal就一定能避免上述的浮点问题吗?来看下面的示例:

上述单元测试中的代码,a和b结果分别是什么?

上面的实例说明,即便是使用BigDecimal,结果依旧会出现精度问题。这就涉及到创建BigDecimal对象时,如果有初始值,是采用new BigDecimal的形式,还是通过BigDecimal#valueOf方法了。

之所以会出现上述现象,是因为new BigDecimal时,传入的0.1已经是浮点类型了,鉴于上面说的这个值只是近似值,在使用new BigDecimal时就把这个近似值完整地保留下来了。

而BigDecimal#valueOf则不同,它的源码实现如下:

在valueOf内部,使用Double#toString方法,将浮点类型的值转换成了字符串,因此就不存在精度丢失问题了。

此时就得出一个基本的第一,在使用BigDecimal构造函数时,尽量传递字符串而非浮点类型;第二,如果无法满足第一条,则可采用BigDecimal#valueOf方法来构造初始化值。

这里延伸一下,BigDecimal常见的构造方法有如下几种:

其中涉及到参数类型为double的构造方法,会出现上述的问题,使用时需特别留意。

第二:浮点精度的坑

如果比较两个BigDecimal的值是否相等,你会如何比较?使用equals方法还是compareTo方法呢?

先来看一个示例:

乍一看感觉可能相等,但实际上它们的本质并不相同。

equals方法是基于BigDecimal实现的equals方法来进行比较的,直观印象就是比较两个对象是否相同,那么代码是如何实现的呢?

仔细阅读代码可以看出,equals方法不仅比较了值是否相等,还比较了精度是否相同。上述示例中,由于两者的精度不同,所以equals方法的结果当然是false了。而compareTo方法实现了Comparable接口,真正比较的是值的大小,返回的值为-1(小于),0(等于),1(大于)。

基本通常情况,如果比较两个BigDecimal值的大小,采用其实现的compareTo方法;如果严格限制精度的比较,那么则可考虑使用equals方法。

另外,这种场景在比较0值的时候比较常见,比如比较BigDecimal("0")、BigDecimal("0.0")、BigDecimal("0.00"),此时一定要使用compareTo方法进行比较。

第三:设置精度的坑

在项目中看到好多同学通过BigDecimal进行计算时不设置计算结果的精度和舍入模式,真是着急人,虽然大多数情况下不会出现什么问题。但下面的场景就不一定了:

执行上述代码的结果是什么?ArithmeticException异常!

这个异常的发生在官方文档中也有说明:

If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.

总结一下就是,如果在除法(divide)运算过程中,如果商是一个无限小数(0.333…),而操作的结果预期是一个精确的数字,那么将会抛出ArithmeticException异常。

此时,只需在使用divide方法时指定结果的精度即可:

执行上述代码,输入结果为0.33。

基本在使用BigDecimal进行(所有)运算时,一定要明确指定精度和舍入模式。

拓展一下,舍入模式定义在RoundingMode枚举类中,共有8种:

通常我们使用的四舍五入即RoundingMode.HALF_UP。

第四:三种字符串输出的坑

当使用BigDecimal之后,需要转换成String类型,你是如何操作的?直接toString?

先来看看下面的代码:

执行的结果是上述对应的值吗?并不是:

也就是说,本来想打印字符串的,结果打印出来的是科学计数法的值。

这里我们需要了解BigDecimal转换字符串的三个方法

三种方法展示结果示例如下:

计算法

基本**根据数据结果展示格式不同,采用不同的字符串输出方法,通常使用比较多的方法为toPlainString()**。

另外,NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。

使用示例如下:

输出结果如下:

小结

本篇文章介绍了BigDecimal使用中场景的坑,以及基于这些坑我们得出的“最佳实践”。虽然某些场景下推荐使用BigDecimal,它能够达到更好的精度,但性能相较于double和float,还是有一定的损失的,特别在处理庞大、复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。而必须使用时,一定要规避上述的坑。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
请大家看看电脑配置怎么样,值多少钱,玩大型网游,页游会不会卡 帮忙看一下旧台式电脑能卖多少钱,谢谢 高考文科考了590,排名3455,福建的,想去外省的本一,能报哪里,急啊= = 华侨大学云南省高考分数为522的能从商学院转建筑学院吗? 厦门华侨大学录取 高考志愿填报应该注意哪些问题 成熟男人戴手表好看吗,成熟男人戴什么手表? 金铲铲之战金色神将阵容怎么搭-金铲铲之战金色神将阵容搭配攻略 金铲铲之战赌蜘蛛阵容推荐 蛛后主C装备搭配攻略 金铲铲之战仙灵蜘蛛阵容搭配推荐 "四脚八叉“是形容什么的啊? 电视剧里有钱人常常在家里用八角杯喝一种白色透明的酒,里面放着... 现代白酒是什么意思? ...正面和背面透明塑料的,里面的酒瓶是白色透明的。 有哪些味道独特的洋酒种类? 透明的酒有哪些 形容姿态多的成语有哪些 忘记自己心爱的人是不是一个错? ...一声“FOG 、COG、COB、CCL”这些东东分别是什么? 我是个大脸男生,身高168,体重124斤,感觉胖,…… 身高1.68体重124斤算胖吗 火锅沾酱爱配生蛋,鸡蛋生吃更营养?营养师破解迷思 回家的人梦见成堆的蛇把凶的那一条打死了? 吃红薯叶有哪些禁忌 t8钢和t10钢有什么区别? 60㎡-120㎡水电改造多少钱?2018水电改造材料多少钱一平方 昆明水电改造工价多少钱一米 云南地理位置(云南是个怎样的省) ...85平方米的电梯公寓,客户应缴纳的维修基金为多少元? GDevelop如何制作平台游戏 仰面朝天什么意思?出处是哪里? 仰面朝天的意思怎么解释 150.9毫米是多少厘米 1厘米䓁于多毫米多少平方毫米 3、09升等于多少立方厘米 ...我的电脑打游戏经常重启。具体游戏是虐杀原形,打极品15倒是正常... 五年级试卷上的作文是写自己亲身经历的事情,我写了我看见别人亲身经历的... 我眼中的开拓者,开疆拓土者,高歌猛进 性生活频率多少合适呢? 大驴是什么车? 梦见去医院看病、有个艾滋病患者,记不得那个人和我什么人关系、性别也... 蚕豆的功效与作用禁忌 蚕豆的营养价值及禁忌 心肺复苏模拟人,我们单位要买两个心肺复苏模拟人作培训用,不知道哪个... 请问深圳西部人才市场在哪里?怎么从西丽坐公交车到那里?谢谢 深圳西部人才市场在哪里 从深圳的《西丽医院东》站坐什么车到深圳西火车站拜托了各位 谢谢... ...区西丽镇西丽医院东站(地铁环中线)怎么样坐车去广州白云区太和谢家... 这些艺术大师的名作为何被“毁容”? 怎么算自己的年龄呢? 简述我国外交政策的基本内容,急急急!!!在考试我国的