BigDecimal详解

文章目录

  • 为什么要使用BigDecimal
  • 构造方法
  • 加减乘除
  • 舍入模式(对BigDecimal进行截取)
  • 取余数
  • 比较大小
  • BigDecimal 转 String
  • BigDecimal与double和int转换
  • 格式化显示 BigDecimal
    • 使用DecimalFormat
    • 使用String.format()
  • 总结

为什么要使用BigDecimal

double类型的数据进行加减乘除,不会得到精确的结果。

public static void main(String[] args) {System.out.println(0.2 + 0.1);System.out.println(0.3 - 0.1);System.out.println(0.2 * 0.1);System.out.println(0.3 / 0.1);}

运行上述的计算代码会得到如下的结果:

0.30000000000000004
0.19999999999999998
0.020000000000000004
2.9999999999999996

那为什么会出现这种情况呢?

原因在于我们的计算机是二进制的。所以 CPU 表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。所以二进制根本无法精确表示浮点数。只能无限接近于精确值而已。

float和double类型的主要设计目标是为了科学计算和工程计算,这些数据类型是为了在广域数值范围上提供快速近似计算而精心设计的。它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候 BigDecimal 就派上大用场啦。

构造方法

1.public BigDecimal(double val)
将 double 表示形式转换为 BigDecimal,不建议使用

2.public BigDecimal(int val)  
将 int 表示形式转换成 BigDecimal

3.public BigDecimal(String val)  
将 String 表示形式转换成 BigDecimal

为什么不建议使用第一种构造器,看下面的例子:

  public static void main(String[] args) {BigDecimal bigDecimal = new BigDecimal(2);BigDecimal bDouble = new BigDecimal(2.3);BigDecimal bString = new BigDecimal("2.3");System.out.println("bigDecimal=" + bigDecimal);System.out.println("bDouble=" + bDouble);System.out.println("bString=" + bString);}

最终输出的结果为:

bigDecimal=2
bDouble=2.29999999999999982236431605997495353221893310546875
bString=2.3

从上面的输出结果可以得知,double 数据转换成 BigDecimal 后并不能得到精确的数据。为什么这样呢?

官方解释说,BigDecimal 的参数类型为 double 的构造方法的结果有一定的不可预知性。在代码中,你看到的2.3,其实只是显示成 2.3 而已,2.3 其实并没有对应的精确二进制数,其对应的二进制数转换成十进制小数实际是 2.29999999999999982236431605997495353221893310546875,只是显示成 2.3 而已。

所以使用 new BigDecimal(2.3); 构造 BigDecimal 对象时,底层参与计算的是对应的二进制数,而这个二进制数转换成十进制数 2.29999999999999982236431605997495353221893310546875,而 BigDecimal 可以精确表示这串数值,所以最终输出的结果就是 2.29999999999999982236431605997495353221893310546875。

另一方面,BigDecimal(String val) 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。

当 double 必须用作 BigDecimal 的源时,请使用 Double.toString(double) 转成 String,然后使用参数类型为 String 的 BigDecimal 构造方法,或使用 BigDecimal 的静态方法 valueOf,如下:

 public static void main(String[] args) {BigDecimal bd1 = new BigDecimal(Double.toString(2.3));BigDecimal bd2 = BigDecimal.valueOf(2.3);System.out.println(bd1);System.out.println(bd2);}

加减乘除

对于常用的加,减,乘,除,BigDecimal 类提供了相应的成员方法:

public BigDecimal add(BigDecimal value);                        //加法public BigDecimal subtract(BigDecimal value);                   //减法 public BigDecimal multiply(BigDecimal value);                   //乘法public BigDecimal divide(BigDecimal value);                     //除法

具体用法如下:

 public static void main(String[] args) {BigDecimal a = new BigDecimal("4.5");BigDecimal b = new BigDecimal("1.5");System.out.println("a + b =" + a.add(b));System.out.println("a - b =" + a.subtract(b));System.out.println("a * b =" + a.multiply(b));System.out.println("a / b =" + a.divide(b));}

注意:因为BigInteger与BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象。

舍入模式(对BigDecimal进行截取)

BigDecimal 除法可能出现不能整除的情况,比如 4.5/1.3,这时会报错:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

其实divide方法有可以传三个参数

public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
第一个参数表示除数;第二个参数表示小数点后保留的位数;第三个参数表示舍入模式,只有在作除法运算或四舍五入时才用到舍入模式。

有下面这几种:

ROUND_CEILING //向正无穷方向舍入。如果为正数,舍入结果同ROUND_UP一致;如果为负数,舍入结果同ROUND_DOWN一致。注意:此模式不会减少数值大小。
ROUND_DOWN //向零方向舍入。舍弃非零部分,不会对非零舍弃部分相邻的数字加一,采取截取行为。
ROUND_FLOOR //向负无穷方向舍入。如果为正数,舍入结果同ROUND_DOWN一致;如果为负数,舍入结果同ROUND_UP一致。注意:此模式不会增加数值大小。
ROUND_HALF_DOWN //向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向下舍入的舍入模式。向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入。这种模式也就是我们常说的我们的“五舍六入”。例如1.55 保留一位小数结果为1.5。
ROUND_HALF_EVEN //向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。即如果舍弃部分左边的数字奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。注意:在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。总结一句话:四舍六入,五分两种情况,如果前一位为奇数,则入位,否则舍去。
ROUND_HALF_UP //向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。即向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入。1.55保留一位小数结果为1.6。这个其实就是四舍五入
ROUND_UNNECESSARY //断言请求的操作具有精确的结果,因此不需要舍入。如果对获得非精确结果的操作指定此舍入模式,则抛出ArithmeticException。
ROUND_UP //向远离0的方向舍入。舍弃非零部分,并将非零舍弃部分相邻的数字加一。例如:4.5633,若保留3位小数,则结果是4.564

按照各自的需要,可传入合适的第三个参数。

从 Java 9.0 开始,不再推荐使用 BigDecimal 的常量,而改成使用枚举类 RoundingMode 的枚举值:

CEILING
DOWN
FLOOR
HALF_DOWN
HALF_EVEN
HALF_UP // 就是四舍五入
UNNECESSARY
UP // 向远离0的方向舍入

需要对 BigDecimal 进行截断,并且四舍五入可用 setScale 方法,例:

public static void main(String[] args) {BigDecimal a = new BigDecimal("4.5635");a = a.setScale(3, BigDecimal.ROUND_HALF_UP);    //保留3位小数,且四舍五入// BigDecimal的常量ROUND_HALF_UP被封装成了RoundingMode的枚举值HALF_UPa = a.setScale(3, RoundingMode.HALF_UP);System.out.println(a);}

取余数

public BigDecimal[] divideAndRemainder(BigDecimal divisor);

该方法接收另一个BigDecimal 对象作为参数,该参数即为除数,返回一个BigDecimal数组,返回数组中包含两个元素,第一个元素为两数相除的商,第二个元素为余数。

使用案例如下:

public static void main(String[] args) {BigDecimal amt = new BigDecimal(14);BigDecimal[] results = amt.divideAndRemainder(BigDecimal.valueOf(5));System.out.println("商:" + results[0]);System.out.println("余:" + results[1]);}

比较大小

BigDecimal a = new BigDecimal (101);
BigDecimal b = new BigDecimal (111);//使用compareTo方法比较
//注意:a、b均不能为null,否则会报空指针
if(a.compareTo(b) == -1){System.out.println("a小于b");
}if(a.compareTo(b) == 0){System.out.println("a等于b");
}if(a.compareTo(b) == 1){System.out.println("a大于b");
}if(a.compareTo(b) > -1){System.out.println("a大于等于b");
}if(a.compareTo(b) < 1){System.out.println("a小于等于b");
}

BigDecimal 转 String

public static void main(String[] args) {// 浮点数的打印System.out.println(new BigDecimal("10000000000").toString());// 普通的数字字符串System.out.println(new BigDecimal("100.000").toString());// 去除末尾多余的0System.out.println(new BigDecimal("100.000").stripTrailingZeros().toString());// 避免输出科学计数法System.out.println(new BigDecimal("100.000").stripTrailingZeros().toPlainString());}

1.用toString()方法输出的就是普通的数字字符串。
2.stripTrailingZeros()函数就是用于去除末尾多余的0的,
3.用toPlainString()函数代替toString(),避免输出科学计数法的字符串。

输出结果为:

10000000000
100.000
1E+2
100

BigDecimal与double和int转换

double 转成 BigDecimal:

double d = 33.45;
BigDecimal b = BigDecimal.valueOf(d);
System.out.println(b);

BigDecimal 转成 double:

BigDecimal b = new BigDecimal("33.67");
double d = b.doubleValue();
System.out.println(d);

格式化显示 BigDecimal

使用DecimalFormat

public static void main(String[] args) {BigDecimal num= new BigDecimal(13.155215);DecimalFormat df1 = new DecimalFormat("0.00"); // 格式也可以写成 #.00String str = df1.format(num);System.out.println(str);// 13.16}

使用String.format()

  public static void main(String[] args) {BigDecimal num= new BigDecimal(13.155215);String result = String.format("%.2f", num);// %.2f - %. 表示小数点前允许任意位数;2表示保留两位小数;f表示格式的结果为浮点型System.out.println(result);}

总结

1.商业计算使用BigDecimal。
2.尽量使用参数类型为String的构造函数。
3.BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。

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

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

相关文章

java序列化和反序列化_Java恶意序列化背后的历史和动机

java序列化和反序列化与Java的序列化机制相关的问题已广为人知。 有效的Java 1st Edition &#xff08;第10章&#xff09;和有效的Java 2nd Edition &#xff08;第11章&#xff09;的整个最后一章都专门讨论Java的序列化主题。 Effective Java 3rd Edition &#xff08;第12章…

python元祖迭代_如何在Python中迭代元组的堆栈

我尝试在Python中使用预定义为的DepthFirstSearch类实现DepthFirstSearch算法&#xff1a;class Stack:def __init__(self):self.list []def push(self,item):self.list.append(item)def pop(self):return self.list.pop()def isEmpty(self):return len(self.list) 0我还有一…

Request的学习笔记(属Servlet学习课程)

文章目录获取请求消息数据1.获取请求行的数据2.获取请求头的数据3.获取请求体的数据4.获取其它的数据4.1.获取请求参数的通用方式中文乱码问题4.2.请求转发4.3.共享数据4.4.获取 ServletContext 对象继承与实现体系浏览器访问服务器时&#xff0c;会将用户提交的参数数据、协议…

aws lambda_AWS Lambda事件源映射:使您的触发器混乱无序

aws lambda最近&#xff0c;我们为Sigma Cloud IDE上的无服务器项目引入了两个新的AWS Lambda事件源&#xff08;触发类型&#xff09;&#xff1a; SQS队列和DynamoDB流 。 &#xff08;是的&#xff0c;AWS在几个月前就向他们介绍了&#xff1b;但是我们仍然是一个很小的团队…

python curl模块_python pycurl模块

一、pycurl概述PycURl是一个C语言写的libcurl的python绑定库。libcurl 是一个自由的&#xff0c;并且容易使用的用在客户端的 URL 传输库。它的功能很强大&#xff0c;在PyCURL的主页上介绍的支持的功能有&#xff1a;FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE and …

MySQL数据库创建用户root@%

步骤&#xff1a; 以 rootlocalhost 登录数据库&#xff0c;即先登录数据库所在的主机&#xff0c;再以 root 用户登录数据库&#xff1a; [roothtlwk0001host ~]# mysql -uroot -p123456必须先删除原来的 root% 用户&#xff1a; mysql> drop user root%; Query OK, 0 r…

第三方工具监控java进程_前9个免费的Java进程监视工具以及如何选择一种

第三方工具监控java进程这样就可以运行Java代码了。 也许它甚至可以在生产服务器上运行。 当您做好工作后&#xff0c;我们得到了好消息和令人讨厌的消息。 令人讨厌的消息是&#xff0c;现在开始调试。 就是进行调试和应用程序性能监视。 这意味着您不仅需要查看编写的代码&a…

python中的numpy函数算相关系数_NumPy ufunc通用函数

NumPy 提供了两种基本的对象&#xff0c;即 ndarray 和 ufunc 对象。前面几节已经介绍了 ndarray&#xff0c;本节将介绍 Numpy。ufunc 是 universal function 的缩写&#xff0c;意思是“通用函数”&#xff0c;它是一种能对数组的每个元素进行操作的函数。许多 ufunc 函数都是…

MySQL 8.x 以前的版本修改密码规则

查看密码相关变量&#xff1a; mysql> show variables like validate_password%; ---------------------------------------------- | Variable_name | Value | ---------------------------------------------- | validate_password_dictionary_f…

aws mfa 认证_如何为您的AWS账户设置多因素身份验证(MFA)

aws mfa 认证第1步 &#xff1a; 转到AWS控制台并使用您的用户名密码登录。 第2步 &#xff1a; 转到服务-> IAM 第三步&#xff1a; 单击您的根帐户上的激活MFA 第4步 &#xff1a; 在步骤3中&#xff0c;点击屏幕上的管理MFA按钮。 步骤5&#xff1a; 单击分配…

python具体工作内容_有没有人知道公司里用python工作的内容有什么

2018-07-11 回答python的特色 简单 python是一种代表简单主义思想的语言。阅读一个良好的python程序就感觉像是在读英语一样&#xff0c;尽管这个英语的要求非常严格&#xff01;python的这种伪代码本质是它最大的优点之一。它使你能够专注于解决问题而不是去搞明白语言本身。 …

如何导入hadoop源码到eclipse

需要进一步学习hadoop、需要看看内部源码实现&#xff0c;因此需要将hadoop源码导入都eclipse中。 简单总结一下&#xff0c;具体步骤如下&#xff1a; 首先确保已经安装了git、maven3、protobuf2.5、如果没有安装需要提前安装一下 0、preliminary linux distribution(我的…

线程池的学习

文章目录线程池原理JDK 1.5 之后提供的线程池工厂类线程池的使用步骤线程池原理 1.创建多个线程对象&#xff0c;存放到集合中&#xff0c;集合可以是 ArrayList 或者 LinkedList 2.从集合中取出一个线程对象&#xff0c;执行指定的任务 3.一个线程对象只能执行一个任务&#…

javafx 界面_JavaFX的科幻用户界面第1部分

javafx 界面使用JavaFX创建的Sci-Fi UI成形窗口 虚构的UI可以变成现实吗&#xff1f; 成长于80年代的小时候&#xff0c;我看到了自己的科幻电影&#xff0c;这确实激发了我对图形用户界面&#xff08;GUI&#xff09;的热爱&#xff0c;尤其是试图使科幻UI在现实世界中成为可…

token验证_如何利用 C# 爬取带 Token 验证的网站数据?

在对文本数据的情感分析中&#xff0c;基于情感词典的方法是最简单也是最常用的一种了。它的大体思路如下&#xff1a;对文档分词&#xff0c;找出文档中的情感词、否定词以及程度副词&#xff0c;然后判断每个情感词之前是否有否定词及程度副词&#xff0c;将它之前的否定词和…

hadoop2.6.0+eclipse配置

【0】安装前的声明 0.1&#xff09; 所用节点2个 master : 192.168.119.105 hadoop5 slave : 192.168.119.101 hadoop1 &#xff08;先用一个slave&#xff0c;跑成功后&#xff0c;在从master分别scp到各个slaves即可】&#xff09; 0.2&#xff09; 每个机子的那些个文件…

线程同步的学习

文章目录一、同步代码块二、同步方法静态同步方法三、Lock解决线程安全问题的三种方案&#xff1a;同步代码块、同步方法、使用 Lock一、同步代码块 synchronized(同步锁对象) { 需要同步操作的代码 }注&#xff1a; 1.锁对象可以是任意对象 2.必须保证多个线程使用同一个锁对…

spock测试_用于混合Spock 1.x和JUnit 5测试的Maven项目设置

spock测试我为Maven创建了一个示例Groovy项目&#xff0c;该项目在一个项目中混合了Spock测试和JUnit 5测试。 在下一节中&#xff0c;我将描述如何设置这种Maven项目。 在项目中启用Groovy 首先&#xff0c;您必须在项目中启用Groovy。 一种可能性是将GMavenPlus插件添加到您…

python 图片识别_python识别图片文字

滑稽研究所python识别图片文字哈喽&#xff0c;大家好呀&#xff0c;我是滑稽君。大家在写论文时可能经常碰到无法复制文字的文章。明明找到了需要的内容却无法直接复制使用&#xff0c;这让我们十分苦恼。那么本期滑稽君就告诉大家如何使用python识别图片中的文字。滑稽君整理…

MySQL数据库的数据类型decimal详解

大概意思是这样的&#xff1a; decimal(m,d) m是数字的最大位数&#xff0c;他的范围是从1-65&#xff1b; d是小数点后的位数&#xff0c;他的范围是0-30&#xff0c;并且不能大于m。 如果m被省略了&#xff0c;那么m的值默认为10&#xff0c; 如果d被省略了&#xff0c;那么d…