Java BigDecimal详解

news2024/11/23 8:22:39

目录

一、前言

二、BigDecimal

2.1、常用构造方法

2.2、常用方法

2.3、示例代码

add(BigDecimal)方法

subtract(BigDecimal)方法

multiply(BigDecimal)方法

divide(BigDecimal)方法

max(BigDecimal val)方法

2.4、BigDecimal的八种舍入模式

setScale(1)

setScale(1,BigDecimal.ROUND_DOWN)

setScale(1,BigDecimal.ROUND_UP)

setScale(1,BigDecimal.ROUND_HALF_UP)

setScaler(1,BigDecimal.ROUND_HALF_DOWN)

setScaler(1,BigDecimal.ROUND_CEILING)

setScaler(1,BigDecimal.ROUND_FLOOR)

setScaler(1,BigDecimal.ROUND_HALF_EVEN)

2.5、注意事项

(1)创建 BigDecimal精度丢失的坑

(2)等值比较的坑

(3)无限精度的坑


一、前言

我们在做浮点数运算时,很多时间计算的结果并不是我们想要的,比如下面的代码:

double y = 1.0 - 0.9;
System.out.println(y);

正常来说,我们想要的结果为0.1,但结果确是:

0.09999999999999998

我们发现,计算出来的值和我们预期结果不一致。原因在于我们的计算机是二进制的。浮点数没有办法使用二进制进行精确表示

计算机的 CPU 表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。

浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。往往产生误差不是因为数的大小,而是因为数的精度。因此,产生的结果接近但不等于想要的结果。尤其在使用 float 和 double 作精确运算的时候要特别小心。

二、BigDecimal

因为浮点类型无法满足我们的精度要求,在做金融行业运算时,我们尽量使用BigDecimal类。

2.1、常用构造方法

                构造方法                                              含义
BigDecimal(double val)创建一个具有参数所指定双精度值的对象。(不推荐使用,因为存在精度丢失问题)
BigDecimal(String val)创建一个具有参数所指定以字符串表示的数值的对象。(推荐使用)

2.2、常用方法

                  方法                                               含义
add(BigDecimal)BigDecimal对象中的值相加,返回BigDecimal对象
subtract(BigDecimal)BigDecimal对象中的值相减,返回BigDecimal对象
multiply(BigDecimal)BigDecimal对象中的值相乘,返回BigDecimal对象
divide(BigDecimal)BigDecimal对象中的值相除,返回BigDecimal对象
abs()将BigDecimal对象中的值转换成绝对值
doubleValue()将BigDecimal对象中的值转换成双精度数
floatValue()将BigDecimal对象中的值转换成单精度数
longValue()将BigDecimal对象中的值转换成长整数
intValue()将BigDecimal对象中的值转换成整数
compareTo(BigDecimal val)比较大小,返回int类型。0(相等) 1(大于) -1(小于)
toString()有必要时使用科学计数法。
toPlainString()不使用任何指数(推荐使用)
toEngineeringString()有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法,与科学技术法类似,但要求10的幂必须是3的倍数
max(BigDecimal val)两值比较,返回最大值
negate()求相反数,正变负,负变正
pow(int n)求乘方,如BigDecimal.valueOf(2).pow(3)的值为8

2.3、示例代码

add(BigDecimal)方法

BigDecimal b1 = new BigDecimal("1.0");
BigDecimal b2 = new BigDecimal("0.9");
BigDecimal b3 = b1.add(b2);
double d1 = b3.doubleValue();
System.out.println(d1);  //1.9

subtract(BigDecimal)方法

BigDecimal b1 = new BigDecimal("1.0");
BigDecimal b2 = new BigDecimal("0.9");
BigDecimal b3 = b1.subtract(b2);
double d1 = b3.doubleValue();
System.out.println(d1);  //0.1

multiply(BigDecimal)方法

BigDecimal b1 = new BigDecimal("1.0");
BigDecimal b2 = new BigDecimal("0.9");
BigDecimal b3 = b1.multiply(b2);
double d1 = b3.doubleValue();
System.out.println(d1);  //0.9

divide(BigDecimal)方法

BigDecimal b1 = new BigDecimal("2.0");
BigDecimal b2 = new BigDecimal("1.0");
BigDecimal b3 = b1.divide(b2);
double d1 = b3.doubleValue();
System.out.println(d1);  //2.0

max(BigDecimal val)方法

BigDecimal b1 = new BigDecimal("2.0");
BigDecimal b2 = new BigDecimal("1.0");
BigDecimal b3 = b1.max(b2);
double d1 = b3.doubleValue();
System.out.println(d1);  //2.0

2.4、BigDecimal的八种舍入模式

setScale(1)

表示保留一位小数,默认用四舍五入方式。

BigDecimal b1 = new BigDecimal("2.6789");
BigDecimal b2 = b1.setScale(1);
System.out.println(b2.doubleValue());

setScale方法默认使用的roundingMode是ROUND_UNNECESSARY,不需要使用舍入模式,设置精度2位,但是小数点后有4位肯定会抛异常。

setScale(1,BigDecimal.ROUND_DOWN)

直接删除多余的小数位,如2.35会变成2.3。

BigDecimal b1 = new BigDecimal("2.6789");
BigDecimal b2 = b1.setScale(1,BigDecimal.ROUND_DOWN);
System.out.println(b2.doubleValue()); //2.6

setScale(1,BigDecimal.ROUND_UP)

进位处理,2.35变成2.4。

BigDecimal b1 = new BigDecimal("2.6789");
BigDecimal b2 = b1.setScale(1,BigDecimal.ROUND_UP);
System.out.println(b2.doubleValue()); //2.7

setScale(1,BigDecimal.ROUND_HALF_UP)

四舍五入,2.35变成2.4。

BigDecimal b1 = new BigDecimal("2.6789");
BigDecimal b2 = b1.setScale(1,BigDecimal.ROUND_HALF_UP);
System.out.println(b2.doubleValue()); //2.7

setScaler(1,BigDecimal.ROUND_HALF_DOWN)

四舍五入,2.35变成2.3,如果是5则向下舍。

BigDecimal b1 = new BigDecimal("2.35");
BigDecimal b2 = b1.setScale(1,BigDecimal.ROUND_HALF_DOWN);
System.out.println(b2.doubleValue()); //2.3

setScaler(1,BigDecimal.ROUND_CEILING)

接近正无穷大的舍入。

BigDecimal b1 = new BigDecimal("2.35");
BigDecimal b2 = b1.setScale(1,BigDecimal.ROUND_CEILING);
System.out.println(b2.doubleValue()); //2.4

setScaler(1,BigDecimal.ROUND_FLOOR)

接近负无穷大的舍入,数字>0和ROUND_UP作用一样,数字<0和ROUND_DOWN作用一样。

setScaler(1,BigDecimal.ROUND_HALF_EVEN)

向最接近的数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。

2.5、注意事项

(1)创建 BigDecimal精度丢失的坑

在BigDecimal 中提供了多种创建方式,可以通过new 直接创建,也可以通过 BigDecimal.valueOf 创建。这两种方式使用不当,也会导致精度问题。如下:

public static void main(String[] args) throws Exception {
   BigDecimal b1 = new BigDecimal(0.1);
   System.out.println(b1);
   BigDecimal b2 = BigDecimal.valueOf(0.1);
   System.out.println(b2);
   BigDecimal b3 = BigDecimal.valueOf(0.111111111111111111111111111234);
   System.out.println(b3);
}

执行结果:

0.1000000000000000055511151231257827021181583404541015625
0.1
0.1111111111111111

上面示例中两个方法都传入了double类型的参数0.1但是 b1 还是出现了精度的问题。造成这种问题的原因是 0.1 这个数字计算机是无法精确表示的,送给 BigDecimal 的时候就已经丢精度了,而 BigDecimal.valueOf 的实现却完全不同。如下源码所示,BigDecimal.valueOf 中是把浮点数转换成了字符串来构造的BigDecimal,因此避免了问题。

public static BigDecimal valueOf(double val) {
   return new BigDecimal(Double.toString(val));
}

结论:

  • 在使用BigDecimal构造函数时,尽量传递字符串而非浮点类型。
  • 如果无法满足第一条,则可采用BigDecimal.valueOf方法来构造初始化值(但是valueOf受double类型精度影响,当传入参数小数点后的位数超过double允许的16位精度还是可能会出现问题的)。

(2)等值比较的坑

一般在比较两个值是否相等时,都是用equals 方法,但是,在BigDecimal 中使用equals可能会导致结果错误,BigDecimal 中提供了 compareTo 方法,在很多时候需要使用compareTo 比较两个值。如下所示:

public static void main(String[] args){
    BigDecimal b1 = new BigDecimal("1.0");
    BigDecimal b2 = new BigDecimal("1.00");
    System.out.println(b1.equals(b2));
    System.out.println(b1.compareTo(b2));
}

执行结果:

false
0

出现此种结果的原因是,equals不仅比较了值是否相等,还比较了精度是否相同。示例中,由于两个值的精度不同,所有结果也就不相同。而 compareTo 是只比较值的大小。返回的值为-1(小于),0(等于),1(大于)。

结论:

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

(3)无限精度的坑

BigDecimal 并不代表无限精度,当在两个数除不尽的时候,就会出现无限精度的坑,如下所示:

public static void main(String[] args){
    BigDecimal b1 = new BigDecimal("1.0");
    BigDecimal b2 = new BigDecimal("3.0");
    b1.divide(b2);
}

执行结果:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.math.BigDecimal.divide(BigDecimal.java:1693)
	at com.demo.controller.Test.main(Test.java:29)

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

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

public static void main(String[] args){
   BigDecimal b1 = new BigDecimal("1.0");
   BigDecimal b2 = new BigDecimal("3.0");
   System.out.println(b1.divide(b2,2, RoundingMode.HALF_UP));//0.33
}

结论:

  • 在使用BigDecimal进行(所有)运算时,尽量指定精度和舍入模式。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CAN接口:Ubuntu系统下CAN接口使用python调用过程

CAN接口&#xff1a;Ubuntu系统下CAN接口使用python调用过程 介绍在Ubuntu系统中通过python程序使用CAN接口。 1. 使用平台 NVIDIA小型边缘设备NX&#xff0c;&#xff08;Orin NX和Xavier NX都可以&#xff09;&#xff0c;系统采用Ubuntu&#xff0c;&#xff08;18.04版本…

ASSY 11994R13PLC模块

可编程性&#xff1a; PLC模块可以通过编程进行定制&#xff0c;以执行特定的控制逻辑和功能。这种可编程性使其非常灵活&#xff0c;可以适应各种应用。 输入/输出&#xff08;I/O&#xff09;&#xff1a; PLC模块通常具有数字输入和输出&#xff0c;以接收和发送信号。某些…

喜报|星瑞格荣获“2022-2023年度国产数据库应用优秀解决方案”奖项

近日&#xff0c;赛迪网为表彰数字赛道上的先行者&#xff0c;联合《数字经济》杂志社和北京科创互联&#xff0c;共同组织以“树立行业标杆&#xff0c;引领服务创新”为中心的“2022-2023年度产业数字服务案例及创新成果征集活动”。该活动旨在鼓励各行业数字化应用技术创新树…

【ag-grid-vue】列定义(Updating Column Definitions)

列定义一节解释了如何配置列。可以在初始设置列之后更改列的配置。本节介绍如何更新列定义。 添加和删除列 可以通过更新提供给网格的列定义列表来添加和删除列。当设置新列时&#xff0c;网格将与当前列进行比较&#xff0c;并计算出哪些列是旧的(要删除)、哪些列是新的(创建…

增加samba用户提示Failed to add entry for user

这个问题我在CSDN上搜到了很多文章&#xff0c;而且都差不多&#xff0c;一开始并没有解决我的问题&#xff0c;不太理解怎么做&#xff0c;经过多次尝试&#xff0c;最后解决了。记录一下具体操作步骤。 第一个是要配置/etc/samba/smb.conf 文件&#xff0c;在文件最后&#x…

技术漫谈第10期 | “百模大战”:向着行业更深处

自21世纪初以来&#xff0c;人工智能&#xff08;AI&#xff09;已经从科幻小说中的概念成长为现实生活中的重要工具。从符号推理到弱人工智能再到大规模深度学习模型&#xff0c;人工智能已经进入大模型时代的新阶段&#xff0c;是科技竞争的制高点&#xff0c;即将改变千行百…

手把手教你安装Git,萌新迈向专业的必备一步

手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 一、版本控制系统是什么&#xff1f;1. 倒霉的小明2. 版本控制系统3. 常见的版本控制系统 二、GitLab 与 GitHub1. GitLab2. GitHub 三、Git安装1. 下载2. 安装3. 验证 四、初学使用1. 本地仓库2. 远程仓库-Github3. 远程…

关于安防视频汇聚平台EasyCVR视频平台的分析以及应用用途

为了保证对园区环境风险进行有效识别&#xff0c;传统视频监控存在视频结构化利用率低的问题&#xff0c;在实际使用过程中&#xff0c;安全管理人员工作效率低下&#xff0c;依靠人工肉眼查看灵活度低&#xff0c;风险漏报概率高&#xff0c;出现异常情况跟踪不及时&#xff0…

post为什么会发送两次请求

今天看掘金的文章&#xff0c;京东面试问了这个问题&#xff0c;我也处于好奇&#xff0c;想了解这块的内容所以做了以下的来了解&#xff1a; 请求是一对请求&#xff0c;有一次请求是预请求&#xff08;预检请求&#xff09;&#xff0c;它的请求方式是options 火狐浏览器可…

long型的时间戳自动转化为字符串

1、自定义序列化类 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase;JacksonStdImpl public class ToDateStringSerializer extends ToStringSerializerBase {public ToDateStringS…

AP9234 9W升压恒流型 DCDC多串LED恒流驱动 2串3串 LED灯串

描述 AP9234是一款由基准电压源、振荡电路、误差放大电路、相位补偿电路、电流限制电路等构成的CMOS升压型DC/DC LED驱动。由于内置了低导通电阻的增强型N沟道功率MOSFET&#xff0c;因此适用于需要高效率、高输出电流的应用电路。另外&#xff0c;可通过在VSENSE端子连接电流…

SpringBoot扩展接口总结

SpringBoot 扩展接口 1、可扩展接口启动调用概述2、可扩展的点逐个说明2.1、ApplicationContextInitializer2.2、BeanDefinitionRegistryPostProcessor2.3、BeanFactoryPostProcessor2.4、InstantiationAwareBeanPostProcessor2.5、SmartInstantiationAwareBeanPostProcessor2.…

【Docker 】Docker 客户端,容器使用,启动容器,启动已停止运行的容器,停止一个容器,进入容器

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

刘强东的低价武器再上膛

电商的光辉正撒向中国的每一个角落。 河北南部的一个村子&#xff0c;74岁的陈奶奶站在大门口&#xff0c;正等待着小哥送货上门&#xff0c;孤身在家的她平时吃的油盐酱醋&#xff0c;喝的奶粉豆浆&#xff0c;都由女儿崔丽丽在网上买。由于京东是村子里唯二可以上门的快递&a…

C++学习|CUDA安装和配置

CUDA安装和配置 Windows下安装CUDAVS项目配置CUDA Windows下安装CUDA 第一步&#xff1a;先看自己NIVIDIA显卡适合什么版本的CUDA。打开电脑的“NIVIDIA控制面板”->系统信息->组件。会看到我的显卡驱动最高支持的CUDA版本是11.4.56。 第二步&#xff1a;去CUDA官网&…

电脑固定资产管理系统

为了更好地管理固定资产&#xff0c;系统管理员划分了相应的用户使用权限。各级管理者在系统中拥有不同的权限&#xff0c;这些权限能够帮助他们更好地查看和管理其管辖的固定资产。 从基层管理者到高级管理者&#xff0c;每一级别的管理者都能够获得相应的授权和管理权…

将Spring boot 项目部署到tomcat服务艰难

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z X Y Z

SVN 集中式版本管理平台

1. SVN 命令行工具下载地址 https://www.visualsvn.com/downloads/ 2. 下载SVN 命令行工具后&#xff0c;解压后就可以了&#xff0c;不需要安装的 3. 将bin 文件添加到系统环境变量中&#xff0c;不知道怎么打开系统环境变量的自己问度娘 4. 添加到系统环境变量中&#xff0c;…

Scala入门,idea关联Scala

Scala 介绍 Scala是一种多规范的编程语言&#xff0c;它结合了面向对象编程&#xff08;OOP&#xff09;和函数式编程&#xff08;FP&#xff09;的特征&#xff0c;Scala的名字源于”Scalable language“&#xff0c;意为”可伸缩语言“。2003年开发的&#xff0c;并在JVM&a…

计算机网络高频面试题解(三)

23. 流量控制 24. 粘包、拆包问题 25. TCP如何解决粘包、拆包问题 26. UDP如何实现TCP可靠传输 27. IP协议 28. IP的两大功能 29. 为什么IP要采用面向无连接&#xff1f; 30. IP地址&#xff08;IPv4地址&#xff09; 31. IP地址的分类 32. 广播地址