Java开发笔记(三十)大小数BigDecimal
前面介绍的BigInteger只能表达任意整数,但不能表达小数,要想表达任意小数,还需专门的大小数类型BigDecimal。如果说设计BigInteger的目的是替代int和long类型,那么设计BigDecimal的目的便是替代浮点型float和双精度型double了。正如它的兄弟BigInteger一般,BigDecimal不存在什么数值范围限制,无论是整数部分还是小数部分,只要你能写得出来,BigDecimal就能表达出来,从此不必担心基本数字类型的精度问题了。
既然同为大数字家族,BigDecimal的绝大部分用法就与BigInteger保持一致,像add方法、subtract方法、abs方法、pow方法等等直接拿来便是,这里不再重复啰嗦了,且看下面BigDecimal的方法调用代码:
// 生成一个指定数值的大小数变量
BigDecimal sevenAndHalf = BigDecimal.valueOf(7.5);
BigDecimal three = BigDecimal.valueOf(3);
// add方法用来替代加法运算符“+”
BigDecimal sum = sevenAndHalf.add(three);
System.out.println("sum="+sum);
// subtract方法用来替代减法运算符“-”
BigDecimal sub = sevenAndHalf.subtract(three);
System.out.println("sub="+sub);
// multiply方法用来替代乘法运算符“*”
BigDecimal mul = sevenAndHalf.multiply(three);
System.out.println("mul="+mul);
// divide方法用来替代除法运算符“/”
BigDecimal div = sevenAndHalf.divide(three);
System.out.println("div="+div);
// remainder方法用来替代取余数运算符“%”
BigDecimal remainder = sevenAndHalf.remainder(three);
System.out.println("remainder="+remainder);
// negate方法用来替代负号运算符“-”
BigDecimal neg = sevenAndHalf.negate();
System.out.println("neg="+neg);
// abs方法用来替代数学库函数Math.abs
BigDecimal abs = sevenAndHalf.abs();
System.out.println("abs="+abs);
// pow方法用来替代数学库函数Math.pow
BigDecimal pow = sevenAndHalf.pow(2);
System.out.println("pow="+pow);
哇噻,难道这么容易就学会使用BigDecimal了吗?仔细看上面的例子代码,被除数是7.5,除数是3,二者相除得到的商为2.5。注意这是除得尽的情况,倘若换个除不尽的情况,例如把除数改成7,7.5除以7结果理应得到一个无限循环小数。可要是运行以下的测试代码,没想到程序竟然运行异常,未能打印那个值为无限循环小数的商。
// 只有一个输入参数的divide方法,要求被除数能够被除数除得尽。
// 倘若除不尽,也就是商为无限循环小数,则程序会异常退出,
// 报错“Non-terminating decimal expansion; no exact representable decimal result.”
BigDecimal seven = BigDecimal.valueOf(7);
BigDecimal divTest = sevenAndHalf.divide(seven);
System.out.println("divTest="+divTest);
虽说大小数能够表示任意范围的小数,但必须是个有限的范围,而不能是无限的范围。由于内存容量是有限的,一个无限循环小数写出来都写不完,要是放到内存就需要无限大小的内存,因此为了让内存能够放得下无限循环小数,只好给该小数指定需要保留的小数位数,也就意味着BigDecimal表示无限循环小数时还是有精度要求的。
除了规定小数部分的保留位数,还需明确多余部分的数字是直接舍弃还是四舍五入?这样对于无限循环小数来说,除法运算的divide方法需要三个输入参数,包括除数、需要保留的小数位数、多余数字的舍入规则。BigDecimal提供的数字舍入规则主要有下列几种:
ROUND_CEILING:往数值较小的方向取整,类似于Math库的ceiling函数。
ROUND_FLOOR:往数值较大的方向取整,类似于Math库的floor函数。
ROUND_HALF_UP:四舍五入取整,若多余的数字等于.5,则前一位进1,类似于Math库的round函数。
ROUND_HALF_DOWN:类似四舍五入取整,区别在于:若多余的数字等于.5,则直接舍弃。
ROUND_HALF_EVEN:如果保留位数的末尾为奇数,则按照ROUND_HALF_UP方式取整。如果保留位数的末尾为偶数,则按照ROUND_HALF_DOWN方式取整。
由上述规则可知,通常情况下的四舍五入应当采取ROUND_HALF_UP方式。于是重新指定了小数精度和舍入规则,改写后大小数的除法运算代码示例如下:
BigDecimal one = BigDecimal.valueOf(100);
BigDecimal three = BigDecimal.valueOf(3);
// 大小数的除法运算,小数点后面保留64位,其中最后一位做四舍五入
BigDecimal div = one.divide(three, 64, BigDecimal.ROUND_HALF_UP);
System.out.println("div="+div);
运行修改后的除法代码,控制台打印的日志结果见下:
div=33.3333333333333333333333333333333333333333333333333333333333333333
可见此时除法计算正常工作,并且结果值的小数部分确实保留到了64位。
上述带三个输入参数的divide方法固然实现了符合精度的除法运算,但若代码存在多处调用divide方法,便意味着该方法后面的精度规则“64, BigDecimal.ROUND_HALF_UP”在每处调用的地方都会出现,这样不但造成代码重复,而且要是变更精度规则还得改动多处。为此Java又提供了工具MathContext,利用该工具可事先指定包含小数精度和舍入规则在内的精度规则,然后把设置好的工具对象传给divide方法就好了。下面是使用MathContext工具辅助除法运算的代码例子:
// 利用工具MathContext,可以把divide方法的输入参数减少为两个
MathContext mc = new MathContext(64, RoundingMode.HALF_UP);
BigDecimal divByMC = one.divide(three, mc);
System.out.println("divByMC="+divByMC);
在大小数的除法中引入精度工具MathContext,至少有两个好处,其一为:只要定义一次,即可多处使用;其二为:若要变更精度规则,只需修改一个地方。
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(三十)大小数BigDecimal的更多相关文章
- Java开发学习(三十六)----SpringBoot三种配置文件解析
一. 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时 ...
- 转:Java开发牛人十大必备网站
原文来自于:http://www.importnew.com/7980.html 以下是我收集的Java开发牛人必备的网站.这些网站可以提供信息,以及一些很棒的讲座, 还能解答一般问题.面试问题等.质 ...
- Java开发牛人十大必备网站
以下是我收集的Java开发牛人必备的网站.这些网站可以提供信息,以及一些很棒的讲座, 还能解答一般问题.面试问题等.质量是衡量一个网站的关键因素,我个人认为这些网站质量都很好.接下来,我会跟大家分享我 ...
- Java开发人员必备十大工具
Java世界中存在着很多工具,从著名的IDE(例如Eclipse,NetBeans和IntelliJ IDEA)到JVM profiling和监视工具(例如JConsole,VisualVM,Ecli ...
- Java开发笔记(十)一元运算符的技巧
前面讲到赋值运算符的时候,提到“x = x+7”可以被“x += 7”所取代,当然Java编程中给某个变量自加7并不常见,常见的是给某变量自加1,就像走台阶,一般都是一级一级台阶地走,犯不着一下子跳上 ...
- Java开发笔记(十九)规律变化的for循环
前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...
- Java学习笔记三十:Java小项目之租车系统
Java小项目之租车系统 一:项目背景介绍: 根据所学知识,编写一个控制台版的“呱呱租车系统” 功能: 1.展示所有可租车辆: 2.选择车型.租车量: 3.展示租车清单,包含:总金额.总载货量以及其车 ...
- Java开发学习(三十)----Maven聚合和继承解析
一.聚合 分模块开发后,需要将这四个项目都安装到本地仓库,目前我们只能通过项目Maven面板的install来安装,并且需要安装四个,如果我们的项目足够多,那么一个个安装起来还是比较麻烦的 如果四个项 ...
- Java开发学习(三十二)----Maven多环境配置切换与跳过测试的三种方式
一.多环境开发 我们平常都是在自己的开发环境进行开发, 当开发完成后,需要把开发的功能部署到测试环境供测试人员进行测试使用, 等测试人员测试通过后,我们会将项目部署到生成环境上线使用. 这个时候就有一 ...
- Java开发学习(三十五)----SpringBoot快速入门及起步依赖解析
一.SpringBoot简介 SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程. 使用了 Spring 框架后已经简化了我 ...
随机推荐
- 显存充足,但是却出现CUDA error:out of memory错误
之前一开始以为是cuda和cudnn安装错误导致的,所以重装了,但是后来发现重装也出错了. 后来重装后的用了一会也出现了问题.确定其实是Tensorflow和pytorch冲突导致的,因为我发现当我同 ...
- 【CSS 第六天】三种简历
1.盒子模型 2.三种简历 利用float和CSS制作内容一致,但是样式不一致的三种简历. 代码 3.效果 style-1 3.2效果2 效果三
- 快速实现office文档在线预览展示(doc,docx,xls,xlsx,ppt,pptx)
微软:https://view.officeapps.live.com/op/view.aspx?src=(输入你的文档在服务器中的地址):
- 离线安装多版本node,使用nvm管理
windows环境下,使用nvm客户以方便地管理多个node版本,但有时候可能需要离线安装node版本. 结合网络搜搜索结果,多次尝试后我成功在离线安装了多个node版本,方法: 1.在其他联网环境下 ...
- 3-2 Hadoop集群伪分布模式配置部署
Hadoop伪分布模式配置部署 一.实验介绍 1.1 实验内容 hadoop配置文件介绍及修改 hdfs格式化 启动hadoop进程,验证安装 1.2 实验知识点 hadoop核心配置文件 文件系统的 ...
- Java中的读写锁
一.读写锁 1.初识读写锁 a)Java中的锁——Lock和synchronized中介绍的ReentrantLock和synchronized基本上都是排它锁,意味着这些锁在同一时刻只允许一个线程进 ...
- git克隆项目出现remote: HTTP Basic: Access denied
换新电脑,重新装了git,从gitlab上面拉公司项目,出现了remote: HTTP Basic: Access denied错误,说验证失败,百度很多说了很多答案,最后试了这种可以,成功拉下来项目 ...
- [Swift]LeetCode971.翻转二叉树以匹配先序遍历 | Flip Binary Tree To Match Preorder Traversal
Given a binary tree with N nodes, each node has a different value from {1, ..., N}. A node in this b ...
- SpringBoot启动如何加载application.yml配置文件
一.前言 在spring时代配置文件的加载都是通过web.xml配置加载的(Servlet3.0之前),可能配置方式有所不同,但是大多数都是通过指定路径的文件名的形式去告诉spring该加载哪个文件: ...
- Oracle绝对秒数转换为时间戳
一般Oracle得到的时间格式为: 1970-01-05 01:23:56.297 为了计算两个时间的差值: 1970-01-05 01:23:56.297 与 1970-01-05 01:24:57 ...