BigDecimal的常见陷阱

文章目录

  • BigDecimal概述
  • BigDecimal常见陷阱
    • 1.使用BigDecimal的构造函数传入浮点数
    • 2.使用equals()方法进行数值比较
    • 3.使用不正确的舍入模式
  • 总结:

BigDecimal概述

BigDecimal 是 Java 中的一个类,用于精确表示和操作任意精度的十进制数。它提供了高精度的数值计算,并且可以避免浮点数计算中常见的精度丢失问题。
它提供了大量的方法来支持基本的数学运算,如加法、减法、乘法、除法等。它还支持比较操作和取整操作,可以设置小数位数、舍入模式等。此外,BigDecimal 还提供了一些其他功能,如转换为科学计数法、格式化输出、判断是否是整数等。
适用场景:需要处理精确计算或防止浮点数计算精度丢失的场景。

BigDecimal常见陷阱

1.使用BigDecimal的构造函数传入浮点数

其实这个问题我们在使用Float、Double等浮点类型进行计算时,也会经常遇到,比如说下面这个代码

@Test
public void bigDecimalDemo1() {
    float float1 = 1;
    float float2 = 0.9f;
    System.out.println(float1 - float2);
}

输出结果是多少呢?0.1?不是,输出结果是0.100000024。因为 0.9 无法被精确表示为有限位数的二进制小数。在转换为二进制时可能会产生近似值。因此,在进行减法运算时,实际上是对近似值进行计算,而不是对准确的 0.9 进行计算。这导致了精度丢失,最终的计算结果也是一个近似值。因此,输出结果不是准确的 0.1,而是一个近似值。
小伙伴肯定能想到使用BigDecimal来避免这个问题,这时候第一个需要避免的陷阱就来了。看以下代码:

@Test
public void bigDecimalDemo2(){
    BigDecimal bigDecimal1 = new BigDecimal(0.01);
    BigDecimal bigDecimal2 = BigDecimal.valueOf(0.01);
    System.out.println("bigDecimal1 = " + bigDecimal1);
    System.out.println("bigDecimal2 = " + bigDecimal2);
}

输出结果如下:

bigDecimal1 = 0.01000000000000000020816681711721685132943093776702880859375
bigDecimal2 = 0.01

观察输出结果我们可以知道,使用BigDecimal时同样会有精度的问题。所以我们在创建BigDecimal对象时,有初始值使用BigDecimal.valueOf()的方式,可以避免出现精度问题。

为什么会出现差异?

在使用new BigDecimal()实际上是将 0.01 转换为二进制近似值,并将其存储为 BigDecimal 对象。因此,结果中存在微小的误差,即输出结果为0.01000000000000000020816681711721685132943093776702880859375。
而BigDecimal.valueOf()不同,其内部是先将double转为String,因此不存在精度问题。

public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO.  This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
}

TIPS:

  1. 使用整数或长整数作为参数构造:
    ○ BigDecimal(int val):使用一个 int 类型的整数值创建 BigDecimal。
    ○ BigDecimal(long val):使用一个 long 类型的整数值创建 BigDecimal。
  2. 使用字符串作为参数构造:
    ○ BigDecimal(String val):使用一个字符串表示的数值创建 BigDecimal。该字符串可以包含整数部分、小数部分和指数部分。
  3. 使用双精度浮点数作为参数构造:
    ○ BigDecimal(double val):使用一个 double 类型的浮点数值创建 BigDecimal。注意,由于浮点数精度可能丢失,建议使用字符串或其他方法构造 BigDecimal,以避免精度损失问题。
  4. 使用基于 BigInteger 的构造方法:
    ○ BigDecimal(BigInteger val):使用一个 BigInteger 对象来创建 BigDecimal。

2.使用equals()方法进行数值比较

日常项目我们是如何进行BigDecimal数值比较呢?使用equals方法还是compareTo方法?如果使用的是equals方法,那就需要注意啦。看一下示例:

@Test
public void bigDecimalDemo3(){
    BigDecimal bigDecimal1 = new BigDecimal("0.01");
    BigDecimal bigDecimal2 = new BigDecimal("0.010");
    System.out.println(bigDecimal1.equals(bigDecimal2));
    System.out.println(bigDecimal1.compareTo(bigDecimal2));
}

输出结果如下:

false
0

观察结果可以知道使用equals比较结果是不相等的;compareTo的结果为0代表两个数相等;
● compareTo实现了Comparable接口,比较的是值的大小,返回的值为-1-小于,0-等于,1-大于。

为什么equals返回的是false?

public boolean equals(Object x) {
    if (!(x instanceof BigDecimal))
        return false;
    BigDecimal xDec = (BigDecimal) x;
    if (x == this)
        return true;
    if (scale != xDec.scale)
        return false;
    long s = this.intCompact;
    long xs = xDec.intCompact;
    if (s != INFLATED) {
        if (xs == INFLATED)
            xs = compactValFor(xDec.intVal);
        return xs == s;
    } else if (xs != INFLATED)
        return xs == compactValFor(this.intVal);

    return this.inflated().equals(xDec.inflated());
}

我们观察equals的实现逻辑可以知道,BigDecimal重写了equals方法,重写后的关键代码:

if (scale != xDec.scale)
        return false;

也就是会比较两个数值的精度,精度不同返回false。

3.使用不正确的舍入模式

使用BigDecimal进行运算时,一定要正确的使用舍入模式,避免舍入误差引起的问题,并且有时候出现结果是无限小数,程序会抛出异常,比如说:

@Test
public void bigDecimalDemo4(){
    BigDecimal bigDecimal1 = new BigDecimal("1.00");
    BigDecimal bigDecimal2 = new BigDecimal("3.00");
    BigDecimal bigDecimal3 = bigDecimal1.divide(bigDecimal2);
    System.out.println(bigDecimal3);
}

输出结果如下:

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

简单的来说,如果在除法运算过程中,其结果是一个无限小数,而操作的结果预期是一个精确的数字,那么将会抛出ArithmeticException异常。
此时,我们只要正确指定结果精度即可:

@Test
public void bigDecimalDemo4(){
    BigDecimal bigDecimal1 = new BigDecimal("1.00");
    BigDecimal bigDecimal2 = new BigDecimal("3.00");
    BigDecimal bigDecimal3 = bigDecimal1.divide(bigDecimal2, 2, RoundingMode.HALF_UP);
    System.out.println(bigDecimal3);
}

输出结果如下:

0.33

TIPS:

● RoundingMode.UP:向远离零的方向舍入
● RoundingMode.DOWN:向靠近零的方向舍入
● RoundingMode.CEILING:向正无穷方向舍入
● RoundingMode.FLOOR:向负无穷方向舍入
● RoundingMode.HALF_UP:四舍五入,如果舍弃部分大于等于 0.5
● RoundingMode.HALF_DOWN:四舍五入,如果舍弃部分大于 0.5
● RoundingMode.HALF_EVEN:银行家舍入法,遵循 IEEE 754 标准

总结:

在这里插入图片描述

● 尽量使用字符串而非浮点类型来构造 BigDecimal 对象,以避免浮点数转换精度问题。
● 如果无法避免使用浮点类型,则可使用 BigDecimal.valueOf 方法来构造初始化值,以确保精确表示。
● 比较两个 BigDecimal 值的大小时,使用 compareTo 方法。如果需要严格限制精度的比较,可以考虑使用 equals 方法。
● 在进行 BigDecimal 运算前,明确指定精度和舍入模式。使用 setScale 方法设置精度,使用 setRoundingMode 方法设置舍入模式。

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

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

相关文章

软件需求的三大层次,逐层细化的注意事项

需求逐层分解和转化是一个持续优化的过程,在这个过程中,我们需要明确软件需求的三大层次,从而帮助项目团队理解组织或客户的高层目标和期望,满足用户的期望和需求,有助于产品的系统设计和开发。 一、软件需求三大层次 …

【django+vue】连接数据库、登录功能

笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~ 【djangovue专栏】 1.【djangovue】项目搭建、解决跨域访问 【djangovue】连接数据库、登录功能 django连接数据库1.安装MySQL驱动程序2.创建数据库3.配置settings.py文件4.创建表5.添加数据 登录功能1.django实现…

国外媒体套餐发稿,快速升级曝光率,数千倍转换率!

在如今数字化时代,国外市场的开拓对企业来说变得越发关键。应对激烈竞争,怎样快速升级曝光率、做到高转换率变成了重要环节。下面我们就详细介绍一种名为“国外媒体套餐内容推广”的思路,根据开发利用国外媒体网络资源,为公司实现…

Linux CentOS 8(DNS的配置与管理)

Linux CentOS 8(DNS的配置与管理) 目录 一、DNS相关知识1.1 DNS简介1.2 DNS的解析原理1.3 DNS解析 二、DNS服务器部署2.1 不使用chroot模式启动DNS2.2 使用chroot模式DNS 三、DNS配置文件详解3.1 主配文件详解3.2 区域数据库文件详解 四、项目实施4.1 主…

分类问题的评价指标

一、logistic regression logistic regression也叫做对数几率回归。虽然名字是回归,但是不同于linear regression,logistic regression是一种分类学习方法。 同时在深度神经网络中,有一种线性层的输出也叫做logistic,他是被输入…

白银投资的升值空间及未来趋势

在投资多样化的今天,人们可选择的投资产品也越来越多。白银作为具有较高价值的贵金属,一直以来便是仅次于黄金的贵金属投资品种,今年来也受到更多投资者的关注。那么白银的升值空间及未来趋势如何?是否值得投资?这边将…

酒店品牌纷纷冲击中高端,东呈集团能否“快人一步”?

过去两年酒店行业加速洗牌,“强者恒强”的马太效应正持续凸显。 报告显示,2022年排名前10名的酒店集团分别为锦江国际、华住、首旅如家、格林、东呈集团、尚美数智、亚朵、德胧、逸柏、都市酒店。以上10家酒店集团客房规模在连锁酒店市场占有率为62.36&…

Django 入门学习总结3

1、创建数据库 打开mysite/settings.py文件,可以看到我们使用Python中已包含的默认的数据库SQLite,也可以使用其他的数据库,如Oracle、Mysql等。里面也包含时区、语言等设置信息。 在使用数据库和表之前,输入下面的命令&#xf…

electron项目开机自启动

一、效果展示&#xff1a;界面控制是否需要开机自启动 二、代码实现&#xff1a; 1、在渲染进程login.html中&#xff0c;画好界面&#xff0c;默认勾选&#xff1b; <div class"intro">开机自启动 <input type"checkbox" id"checkbox&quo…

01【SpringBoot快速入门、yml语法、自动配置、整合框架】

目录 一、SpringBoot简介 1.1 Spring优缺点 1.1.1 Spring的优点 1.1.2 Spring的缺点 1.2 SpringBoot的概述 1.2.1 SpringBoot概述 1.2.2 SpringBoot的核心功能 二、SpringBoot快速入门 2.1 创建Maven工程 2.2 添加起步依赖 2.3 编写Controller 2.4 编写SpringBoot引…

论文《A recurrent latent variable model for sequential data》笔记:详解VRNN

A recurrent latent variable model for sequential data 背景 1 通过循环神经网络的序列建模 循环神经网络&#xff08;RNN&#xff09;可以接收一个可变长度的序列 x ( x 1 , x 2 , . . . , x T ) x (x_1, x_2, ..., x_T) x(x1​,x2​,...,xT​)作为输入&#xff0c;并通…

JavaWeb开发——文件上传

1 简介 文件上传&#xff1a;将本地图片、视频、音频等文件上传到服务器&#xff0c;供其他用户浏览或下载的过程 文件上传涉及到两部分&#xff1a;前端程序 服务端程序 前端程序 【三要素】&#xff1a;① 需要定义一个form 表单&#xff0c;且表单里需定义一个类型为“ …

Qt专栏3—Qt项目创建Hello World

setp1 打开软件 双击Qt Creator 11.0.3 (Community)&#xff0c;打进入软件界面 step2 创建项目 点击创建项目 step3 选择模板 选着Application&#xff08;Qt&#xff09;->Qt Widgets Application setp4 设置项目 名称中填入项目号名&#xff0c;创建路径中填入项目保存位…

基于C#实现KMP算法

一、BF 算法 如果让你写字符串的模式匹配&#xff0c;你可能会很快的写出朴素的 bf 算法&#xff0c;至少问题是解决了&#xff0c;我想大家很清楚的知道它的时间复杂度为 O&#xff08;MN&#xff09;&#xff0c;原因很简单&#xff0c;主串和模式串失配的时候&#xff0c;我…

django restful framework序列化与反序列化

在前后端分离开发中&#xff0c;对于RESTfulAPI设置&#xff0c;一般需要将查询/更新数据以JSON方式进行返回。 序列化 Model.py from django.db import models class User(models.Model):username models.CharField(verbose_name用户名,max_length10)age models.IntegerF…

服务器数据恢复—raid5上层NTFS分区误删除/格式化的数据恢复案例

NTFS是windows操作系统服务器应用最为广泛的文件系统之一。理论上&#xff0c;NTFS文件系统格式化操作虽然不会对数据造成太大的影响&#xff0c;但是有可能会出现部分文件目录结构丢失的情况。下面介绍一台服务器误操作导致raid5阵列上层的NTFS分区被格式化后如何逆向操作恢复…

Altium Designer学习笔记4

学会添加库。 元器件添加成功。 放置TYPE-C元器件。 绘制网络标识和电源端口&#xff0c;并且添加文字备注。 修改元器件的属性。

Hive安装配置 - 本地模式

文章目录 一、Hive运行模式二、安装配置本地模式Hive&#xff08;一&#xff09;安装配置MySQL1、删除系统自带的MariaDB2、上传MySQL组件到虚拟机3、在主节点上安装MySQL组件4、在主节点上配置MySQL&#xff08;1&#xff09;查看MySQL服务状态&#xff08;2&#xff09;查看M…

Softing mobiLink助力过程自动化——兼容HART、FF、PA的多协议接口工具

由于全球人口增加和气候变化等因素&#xff0c;“水”比以往任何时候都更具有价值。与此同时&#xff0c;环境法规和水处理标准也变得愈加严格。在这一大环境下&#xff0c;自来水公司不得不应对一些新的挑战&#xff0c;例如&#xff0c;更好地提高能源效率、最大程度地减少资…

【完全攻略】Gradio:建立机器学习网页APP

目录 前言一、Gradio介绍以及安装1-1、Gradio介绍1-2、安装 二、快速开始&#xff08;初步了解&#xff09;2-1、简单小栗子2-2、多输入多输出2-3、简易聊天机器人 三、关键技术3-1、带有样例的输入3-2、提示弹窗3-3、描述内容3-4、风格3-5、流式输出3-6、进度条3-7、分享APP 总…