血的教训,BigDecimal踩过的坑

很多人都用过Java的BigDecimal类型,但是很多人都用错了。如果使用不当,可能会造成非常致命的线上问题,因为这涉及到金额等数据的计算精度。

首先说一下,一般对于不需要特别高精度的计算,我们使用double或float类型就可以了。

由于计算机天生的无法表达完整的二进制浮点数的小数,二进制的小数是无限循环的,所以只能无限接近于精确值,这就造成了浮点计算的精度问题。此时就需要使用BigDecimal类型了。

踩坑一:初始化

关于浮点数精度的问题,我们可以看下这个代码。

打印结果会是0.2吗?不是,打印结果是0.19999999。因为b最大化接近于0.8,可能是0.80000001,近似于0.8。这就是为什么说精度要求不高时可以用double或float类型,一旦涉及到金额就不能使用浮点类型的原因。

知道了这个原理,我们使用BigDecimal能否避免浮点计算的精度问题?看下这个代码。

打印结果如下:

使用BigDecimal构造函数和使用valueOf方法初始化,两种方式得到的结果不一样,valueOf方法初始化的BigDecimal数据计算是精确的。我们看下源码就能明白了。

valueOf方法会把浮点数先转换成字符串,再用BigDecimal构造函数初始化。所以就不存在精度问题了。当然,这里要特别注意的是,valueOf方法对double类型的值可以保证精度,但是如果传的是float类型,例如0.8f,则依然会有精度问题。

踩坑结论:

  • 使用BigDecimal构造函数时,传字符串而不要传浮点类型。
  • 尽量使用valueOf方法初始化,并且不要传float类型数据。

踩坑二:比较大小

两个BigDecimal值比较是否相等,是使用equals方法还是使用compareTo方法?先来看个例子。

打印结果:

equals判断a和b不相等,compareTo判断a和b相等。我们看下equals的源码。

equals除了比较值的大小,还会比较值的精度。

踩坑结论:

  • 比较两个BigDecimal值的大小,使用compareTo方法。
  • 比较大小且限制精度,使用equals方法。

踩坑三:除法设置精度

在进行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."

翻译过来的意思是:

"如果商具有非终止的十进制展开,并且指定运算返回精确的结果,则引发ArithmeticException。否则,将返回除法的确切结果,就像对其他操作所做的那样。"

用人话解释上述文字,意思就是如果divide方法计算得到的商是一个无限小数,而代码预期得到一个精确数字,那么就会抛出ArithmeticException异常。

正确的使用divide代码如下。

打印结果:

设置了c的精度为2,RoundingMode.HALF_UP是向上舍入模式,即四舍五入。

踩坑结论:使用BigDecimal进行计算时,结果一定要设置精度和舍入模式。

踩坑四:字符串转换

想将BigDecimal类型转成字符串,用toString方法?先来看个示例。

打印结果:

可以看出,toString方法将BigDecimal的值转成了科学计数法的值。如果想要转成正常的数值应该使用什么方法呢?我们可以先看下BigDecimal三种转字符串的方法。

  • toString():如果需要指数,则使用科学计数法。
  • toPlainString():不带指数的字符串表现形式。
  • toEngineeringString():如果需要指数,则使用工程计数法。

踩坑结论:结合具体业务选择适用的字符串转换方法。

最后,关于数值格式化的功能,我们可以利用NumberFormat类,对BigDecimal进行格式化控制。代码示例如下。

打印结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/237808.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【lesson7】数据类型之string类型

文章目录 数据类型分类string类型set类型测试 enum类型测试 string类型的内容查找找所有女生(enum中)找爱好有游泳的人(set中)找到爱好中有足球和篮球的人 数据类型分类 string类型 set类型 说明: set:集…

nrfutil工具安装

准备工作,下载相关安装包 链接:https://pan.baidu.com/s/1LWxhibf8LiP_Cq3sw0kALQ 提取码:2dlc 解压后,分别安装以下安装包 在C盘下创建目录nordic_tools,并将nrfutil复制到刚创建的目录下 环境变量path下添加C:\nor…

【截图版本】Linux常用指令详解

———————————————— 版权声明:本文为CSDN博主「小呆瓜历险记」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_58963318/article/details/134713282

ProroBuf C++笔记

一.什么是protobuf Protocol Buffers是Google的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤于(数据)通信协议、数据存储等。Protocol Buffers 类⽐于XML,是⼀种灵活,⾼效,⾃动化机制的结…

51单片机应用从零开始(十一)·数组函数、指针函数

51单片机应用从零开始(九)数组-CSDN博客 51单片机应用从零开始(十)指针-CSDN博客 目录 1. 用数组作函数参数控制流水花样 2. 用指针作函数参数控制 P0 口 8 位 LED 流水点亮 1. 用数组作函数参数控制流水花样 要在51单片机中…

0012Java安卓程序设计-ssm记账app

文章目录 **摘要**目 录系统设计5.1 APP端(用户功能)5.2后端管理员功能模块开发环境 编程技术交流、源码分享、模板分享、网课分享 企鹅🐧裙:776871563 摘要 网络的广泛应用给生活带来了十分的便利。所以把记账管理与现在网络相…

GaussDB数据库语法及gsql入门

一、GaussDB数据库语法入门 之前我们讲了如何连接数据库实例,那连接数据库后如何使用数据库呢?那么我们今天就带大家了解一下GaussDB,以下简称GaussDB的基本语法。 关于如何连接数据库,请戳这里。 学习本节课程之后&#xff0c…

【金华模式】双龙旅游引燃露营设计和文旅产融合新方式

(中国国际教育电视台 黎明)金华双龙风景旅游区位于浙江省金华市北郊的金华山麓,是一处融自然山水、溶洞群景观、科普探险、康体休闲、避暑度假、观光朝圣于一体的景区。旅游区人文积淀深厚,道、儒、释文化兼收并蓄,东汉…

大语言模型有什么意义?亚马逊训练自己的大语言模型有什么用?

近年来,大语言模型的崭露头角引起了广泛的关注,成为科技领域的一项重要突破。而在这个领域的巅峰之上,亚马逊云科技一直致力于推动人工智能的发展。那么,作为一家全球科技巨头,亚马逊为何会如此注重大语言模型的研发与…

Blender学习:走路机器人,骨骼绑定

文章目录 建模骨骼创建骨骼绑定 教程地址:八个案例教程带你从0到1入门blender【已完结】 建模 1 做头:新建立方体,Ctrl2细分并应用,进入编辑模式,删除一半点,然后添加镜像修改器,开启范围限制…

软件测试20个基础面试题及答案

什么是软件测试? 答案:软件测试是指在预定的环境中运行程序,为了发现软件存在的错误、缺陷以及其他不符合要求的行为的过程。 软件测试的目的是什么? 答案:软件测试的主要目的是保证软件的质量,并尽可能…

python socket编程9 - PyQt6界面实现UDP server/client 多客户端通讯的例子

本篇实现 UDP server和client多客户端通讯的例子。 在UDP单机通讯的基础上进行重构,实现UDP server与多个 client通讯的例子。 创建两个 PyQt6的项目,一个作为UDP server 项目,另一个作为UDP client项目。 一、效果图 1、udp server界面 …

在线学习平台-课程分页、用户管理、教师查询

在线学习平台------手把手教程👈 用户管理 添加功能增强 新增属性 若依里的用户模块(SysUser)是没有课程这一属性的,要实现我们自己的课程分页查询功能 这个位置传入的实体类SysUser要加上classId,记得加上get、set方法 更改sql语句 ctrl 鼠标左键不断点进去…

使用Gson完成java对象的序列化和反序列化

一、前言:json是什么?,Gson是什么? 1.JSON(javaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写,同时也易于机器解析和生成。 2.Gson 是Google提供的用来在java对象…

Nextui使用

安装和使用 https://nextui.org/docs/frameworks/nextjs 自定义主题 https://nextui.org/docs/customization/customize-theme // tailwind.config.js const {nextui} require("nextui-org/react");/** type {import(tailwindcss).Config} */ module.exports {…

mysql:用SHOW CREATE TABLE tbl_name查看创建表的SQL语句

https://dev.mysql.com/doc/refman/8.2/en/show-create-table.html 可以用SHOW CREATE TABLE tbl_name查看创建表的SQL语句。 例如,SHOW CREATE TABLE test_table;表示查询创建test_table表的SQL语句:

day23_jdbc详解

JDBC JDBC概述 什么jdbc Java DataBase Connectivity是一种用于执行SQL语句的Java API,它由一组用Java语言编写的类和接口组成。通过这些类和接口,JDBC把SQL语句发送给不同类型的数据库进行处理并接收处理结果。 jdbc的作用 提供java 操作不同数据库…

HTML面试题---专题一

文章目录 一、前言二、 HTML5 中 <header> 和 <footer> 标签的用途是什么&#xff1f;三、如何在 HTML 中嵌入 SVG&#xff08;可缩放矢量图形&#xff09;文件&#xff1f;四、解释 contenteditable 属性的用途五、如何创建随屏幕尺寸缩放的响应式图像&#xff1f…

虚幻学习笔记11—C++结构体、枚举与蓝图的通信

一、前言 结构体的定义和枚举类似&#xff0c;枚举的定义有两种方式。区别是结构体必须以“F”开头命名&#xff0c;而枚举不用。 额外再讲了一下蓝图生成时暴露变量的方法。 二、实现 2.1、结构体 1、定义结构体 代码如下&#xff0c;注意这个定义的代码一定要在“UCLASS()”…

如何将用户有过行为的item用list形式记录下来,另外如何计算list里的个数

导语&#xff1a; 最近做项目&#xff0c;发现有些语法想一想是知道&#xff0c;但实际操作起来跟想的情况不一样哈哈。不是遇见bug就是输出的结果不是自己想要的&#xff0c;CSDN跟知乎找了很多没怎么解决&#xff0c;后面多摸索多实操终于解决&#xff01; test_data[item_…