生产环境BigDecimal用错了,已哭晕在厕所。。。
大家好,我是苏三,又跟大家见面了。
前言
在日常开发中,很多小伙伴喜欢用 BigDecimal 来处理精确计算,比如钱、分数、比例啥的。
理论上,它比 double 或 float 更精确,但如果你用得不对,精度丢失的问题会让你哭晕在厕所。
今天我们就来聊聊 ,错误使用BigDecimal的6种场景,为什么会发生问题,以及怎么避免问题,希望对你会有所帮助。
1 直接用浮点数初始化
不少小伙伴习惯这样写:
BigDecimal num = new BigDecimal(0.1);
System.out.println(num);
打印结果:0.1000000000000000055511151231257827021181583404541015625
并非打印的:0.1
问题出在哪?
这不是 BigDecimal 的问题,而是浮点数本身的“锅”。
在Java中,double的精度有限的,0.1 转换成二进制是个无限循环小数,直接传进去会带上误差。
正确姿势是传字符串:
BigDecimal num = new BigDecimal("0.1");
System.out.println(num);
打印结果:0.1,是正确的。
注意:永远不要用 BigDecimal(double) 构造函数,用字符串或整数更靠谱。也可以使用BigDecimal.valueOf()函数。
2 加减乘除时不设精度
有些小伙伴做加减乘除的时候,直接写:
BigDecimal a = new BigDecimal("1.03");
BigDecimal b = new BigDecimal("0.42");
//减法
BigDecimal result = a.subtract(b);
System.out.println(result);
打印结果::0.61,没问题。
但问题在 除法 时:
BigDecimal c = new BigDecimal("10");
BigDecimal d = new BigDecimal("3");
BigDecimal result = c.divide(d);
运行直接炸了:java.lang.ArithmeticException: Non-terminating decimal expansion
报错的根本原因:10/3 是无限小数,BigDecimal 默认不保留小数点后面,精度溢出。
那么,我们要如何优化呢?
答:加一个 MathContext 或指定精度。
例如:
BigDecimal result = c.divide(d, 2, RoundingMode.HALF_UP);
System.out.println(result);
打印结果:3.33,可以正常运行。
因此,我们需要注意,在BigDecimal 做除法时 ,必须指定精度。
3 用 equals 判断相等
BigDecimal 的 equals 会比较 值和精度,这坑了不少人:
BigDecimal x = new BigDecimal("1.0");
BigDecimal y = new BigDecimal("1.00");
System.out.println(x.equals(y));
打印结果:false。
尽管 1.0 和 1.00 的数值相等,但精度不一样,equals 判定为不同。
优化方法,用 compareTo 比较数值:
例如:
System.out.println(x.compareTo(y) == 0);
打印结果:true
需要特别注意的地方是:我们在判断两个BigDecimal对象是否相等时,应该用 compareTo方法,别用 equals方法。
4 使用 scale 时忽视实际含义
有些小伙伴搞不清 scale(小数位数)和 precision(总位数)的区别,直接写:
BigDecimal num = new BigDecimal("123.4500");
System.out.println(num.scale());
打印结果:4
但如果你写成下面这样的:
BigDecimal stripped = num.stripTrailingZeros();
System.out.println(stripped.scale());
打印结果却是:2
scale 会发生变化,搞不好会影响后续计算。
那么,我们要如何优化方法呢?
答:明确 scale 的含义。
如果要固定小数位,使用 setScale:
BigDecimal fixed = num.setScale(2, RoundingMode.HALF_UP);
System.out.println(fixed);
打印结果:123.45。
我们不要混淆 scale 和 precision,必要时显式设置小数位数。
5 忽略不可变性
BigDecimal 是不可变的,但有些小伙伴会这样写:
BigDecimal sum = new BigDecimal("0");
for (int i = 0; i < 5; i++) {
sum.add(new BigDecimal("1"));
}
打印结果:0
问题原因是 add 方法不会改变原对象,而是返回一个新的 BigDecimal 实例。
那么,我们要如何优化呢?
答:用变量接住返回值。
BigDecimal sum = new BigDecimal("0");
for (int i = 0; i < 5; i++) {
sum = sum.add(new BigDecimal("1"));
}
System.out.println(sum);
打印结果是:5
BigDecimal 操作后需要接住新实例。
6 忽视性能问题
BigDecimal 是很精确,但也很慢。
如果大量计算时用 BigDecimal,会拖累性能,比如计算利息:
BigDecimal principal = new BigDecimal("10000");
BigDecimal rate = new BigDecimal("0.05");
BigDecimal interest = principal.multiply(rate);
一个循环里搞上百万次,性能直接拉垮。
那么,这种情况我们又该如何优化呢?
答:能用整数就用整数(比如分代替元)。
批量计算时,用 double 计算,结果最后转换成 BigDecimal。
double principal = 10000;
double rate = 0.05;
BigDecimal interest = BigDecimal.valueOf(principal * rate);
System.out.println(interest);
打印结果:500.00
参与大批量计算时,两个BigDecimal对象直接计算会比较慢,尽量少用,能优化的地方别放过。
写在最后
BigDecimal 是个非常强大的数字类工具,但也是个“细节狂魔”。
只有用对了,你才能真正享受它带来的好处,否则就是自找麻烦。
希望这篇文章能帮到你,不要再踩坑。
如果有其他用法上的困惑,欢迎留言讨论,我们一起成长!
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。
生产环境BigDecimal用错了,已哭晕在厕所。。。的更多相关文章
- (转) 将ASP.NET Core应用程序部署至生产环境中(CentOS7)
原文链接: http://www.cnblogs.com/ants/p/5732337.html 阅读目录 环境说明 准备你的ASP.NET Core应用程序 安装CentOS7 安装.NET Cor ...
- 将ASP.NET Core应用程序部署至生产环境中(CentOS7)
这段时间在使用Rabbit RPC重构公司的一套系统(微信相关),而最近相关检验(逻辑测试.压力测试)已经完成,接近部署至线上生产环境从而捣鼓了ASP.NET Core应用程序在CentOS上的部署方 ...
- 将ASP.NET Core应用程序部署至生产环境中(CentOS7)(转)
阅读目录 环境说明 准备你的ASP.NET Core应用程序 安装CentOS7 安装.NET Core SDK for CentOS7. 部署ASP.NET Core应用程序 配置Nginx 配置守 ...
- 生产环境中CentOS7部署NET Core应用程序
NET Core应用程序部署至生产环境中(CentOS7) 阅读目录 环境说明 准备你的ASP.NET Core应用程序 安装CentOS7 安装.NET Core SDK for CentOS7. ...
- 中小研发团队架构实践之生产环境诊断工具WinDbg 三分钟学会.NET微服务之Polly 使用.Net Core+IView+Vue集成上传图片功能 Fiddler原理~知多少? ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一) C#程序中设置全局代理(Global Proxy) WCF 4.0 使用说明 如何在IIS上发布,并能正常访问
中小研发团队架构实践之生产环境诊断工具WinDbg 生产环境偶尔会出现一些异常问题,WinDbg或GDB是解决此类问题的利器.调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具 ...
- CentOS7.1下生产环境Keepalived+Nginx配置
CentOS7.1下生产环境Keepalived+Nginx配置 [日期:2015-07-20] 来源:Linux社区 作者:soulful [字体:大 中 小] 注:下文涉及到配置的,如无特别 ...
- require.js+backbone 使用r.js 在本地与生产环境 一键压缩的实现方式
require.js+backbone 使用r.js 在本地与生产环境 一键压缩的实现方式 时间:2017-07-03 17:18:11 阅读:210 评论:0 收藏:0 ...
- 生产环境常见的HTTP状态码列表
生产环境常见的HTTP状态码列表(List of HTTP status codes)为: 200 - OK,服务器成功返回网页 - Standard response for success ...
- 理解Docker(6):若干企业生产环境中的容器网络方案
本系列文章将介绍 Docker的相关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...
- .Net Core Linux centos7行—发布程序到生产环境
实验demo现在需要发布到生产环境,发现在发布的时候要考虑到不一致的几个地方. 1.各类配置文件线下,线上不一致. 2.绑定的url不一致,可能是域名不一致,也可能是schema不一致(http,ht ...
随机推荐
- MyBatisPlus——DML编程控制——乐观锁
乐观锁 业务并发现象带来的问题:秒杀最后一单 用于中小型项目(2000请求以下) 添加一个数据库字段,使每次请求修改数据时,这个字段就加一,当有多人同时请求时,这些人同时获取到的都是相同的该字段,但当 ...
- QT6窗口系统之QT底层窗口QWindow:QT框架中哪些常见窗口是基于QWindow的? 如何实现QT框架栅格窗口?如何实现QT框架OpenGL窗口?
QT6窗口系统之QT底层窗口QWindow:QT框架中哪些常见窗口是基于QWindow的? 如何实现QT框架栅格窗口?如何实现QT框架OpenGL窗口? 简介 本文介绍了QT6窗口系统中的QT底层窗口 ...
- 参与 2023 第一季度官方 Flutter 开发者调查
Flutter 3.7 已经正式发布,每个季度一次的 Flutter 开发者调查也如约而至,邀请社区的各位成员们填写! 调查表链接: https://flutter.cn/urls/2023q1wx ...
- 关于 xfg 的班会
- SuperMap iDesktopX创建HBase数据源并导入数据
需提前部署HBase集群,HBase环境搭建请查看文章https://www.cnblogs.com/zhangyongli2011/p/12034628.html 本文基于10.1.1 win版本s ...
- 2024年8月中国数据库排行榜:OceanBase攀升再夺冠,达梦跃入三甲关
在这个炽热的季节,随着巴黎奥运会的盛大开幕,全球将目光聚集在了体育的无限魅力和竞技的巅峰对决上.如同奥运赛场上的激烈角逐,中国数据库界也上演着一场技术与创新的较量,各个数据库产品正在中国乃至全球舞台上 ...
- 基于腾讯云短信接口和nodejs服务器实现手机号验证码
知识储备:js基础.nodejs基础.ajax基础: 1. 手机验证码原理 表单提交,把手机号码传送到后端:后端拿到手机号码后根据相关算法随机形成一个验证码,并将其保存在数据库:用户拿到验证码后将验证 ...
- 65.说下vue3的使用感想(说些vue3对比vue3的方便之处)
vue3 使用了组合式API,setup 替换了选项式api ,不需要在多个api里面写代码了,而且使用了setup的语法糖,可以更加方便写代码 : vue3使用proxy替代了Object.defi ...
- 云原生周刊:Kubernetes v1.28 正式发布 | 2023.8.21
开源项目推荐 kurt 一个 Kubernetes 插件,可提供 Kubernetes 集群中重启内容的上下文信息. Kubean Kubean 是一个基于 kubespray 的 Kubernete ...
- 进程相互作用之信号量PV操作及其代码实现
目录 信号量PV操作 基本介绍 数据结构 解决进程互斥问题 解决进程同步问题 代码实现(以同步问题为例) 信号量PV操作 基本介绍 信号量(Semaphore):是表示资源的实体,是一个与队列有关的整 ...