前面介绍的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的更多相关文章

  1. Java开发学习(三十六)----SpringBoot三种配置文件解析

    一. 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时 ...

  2. 转:Java开发牛人十大必备网站

    原文来自于:http://www.importnew.com/7980.html 以下是我收集的Java开发牛人必备的网站.这些网站可以提供信息,以及一些很棒的讲座, 还能解答一般问题.面试问题等.质 ...

  3. Java开发牛人十大必备网站

    以下是我收集的Java开发牛人必备的网站.这些网站可以提供信息,以及一些很棒的讲座, 还能解答一般问题.面试问题等.质量是衡量一个网站的关键因素,我个人认为这些网站质量都很好.接下来,我会跟大家分享我 ...

  4. Java开发人员必备十大工具

    Java世界中存在着很多工具,从著名的IDE(例如Eclipse,NetBeans和IntelliJ IDEA)到JVM profiling和监视工具(例如JConsole,VisualVM,Ecli ...

  5. Java开发笔记(十)一元运算符的技巧

    前面讲到赋值运算符的时候,提到“x = x+7”可以被“x += 7”所取代,当然Java编程中给某个变量自加7并不常见,常见的是给某变量自加1,就像走台阶,一般都是一级一级台阶地走,犯不着一下子跳上 ...

  6. Java开发笔记(十九)规律变化的for循环

    前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...

  7. Java学习笔记三十:Java小项目之租车系统

    Java小项目之租车系统 一:项目背景介绍: 根据所学知识,编写一个控制台版的“呱呱租车系统” 功能: 1.展示所有可租车辆: 2.选择车型.租车量: 3.展示租车清单,包含:总金额.总载货量以及其车 ...

  8. Java开发学习(三十)----Maven聚合和继承解析

    一.聚合 分模块开发后,需要将这四个项目都安装到本地仓库,目前我们只能通过项目Maven面板的install来安装,并且需要安装四个,如果我们的项目足够多,那么一个个安装起来还是比较麻烦的 如果四个项 ...

  9. Java开发学习(三十二)----Maven多环境配置切换与跳过测试的三种方式

    一.多环境开发 我们平常都是在自己的开发环境进行开发, 当开发完成后,需要把开发的功能部署到测试环境供测试人员进行测试使用, 等测试人员测试通过后,我们会将项目部署到生成环境上线使用. 这个时候就有一 ...

  10. Java开发学习(三十五)----SpringBoot快速入门及起步依赖解析

    一.SpringBoot简介 SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程. 使用了 Spring 框架后已经简化了我 ...

随机推荐

  1. Bulk API

    承接上文,使用Java High Level REST Client操作elasticsearch Bulk API 高级客户端提供了批量处理器以协助批量请求 Bulk Request BulkReq ...

  2. framework7 入门(数据绑定)

    数据绑定是用template7,官网,framework7内置template7,不用单独下载,下面介绍一些简单的绑定 当你的数据是个数组 return{ data:function(){ let i ...

  3. 控件包含代码块(即 <% ... %>),因此无法修改控件集合。

    原因分析:在head里写的js代码中包含了<%=...%>代码 解决:把js的代码放到body中...

  4. vs2017 调试时 浏览器关闭不想中断调试

    解决方案 工具—>选项—>项目和解决方案—>web项目-->去点“浏览器窗口关闭时停止调试”前面的勾去掉>>>

  5. H5 和移动端 WebView 缓存机制解析与实战

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/qHm_dJBhVbv0pJs8Crp77w 作者:叶 ...

  6. 亿级SQL Server运维的最佳实践PPT分享

    这次分享是我在微软的一次分享,关于SQL Server运维最佳实践的部分,由于受众来自不同背景,因此我让分享在一个更加抽象的角度进行,PPT分享如下: 点击这里进行下载

  7. [Swift]LeetCode121. 买卖股票的最佳时机 I | Best Time to Buy and Sell Stock

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  8. [Bash]LeetCode193. 有效电话号码 | Valid Phone Numbers

    Given a text file file.txt that contains list of phone numbers (one per line), write a one liner bas ...

  9. postgresql 删除库的时候报错database "temp_test_yang" is being accessed by other users

    删除库的时候报错 ERROR: database "temp_test_yang" is being accessed by other usersDETAIL: There ar ...

  10. 微信小程序中样式问题

    1.去除button按钮的默认样式 这是button按钮自带的默认样式 button { position:relative; display:block; margin-left:auto; mar ...