前面介绍的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. 连接Redis_五种数据格式

    前面我们已经准备成功开启Redis服务,其端口号为6379,接下来我们就看看如何使用C#语言来操作Redis.就如MongoDB一样,要操作Redis服务,自然就需要下载C#的客户端,这里通过Nuge ...

  2. IntelliJ IDEA配置Tomcat和Lombok

    Tomcat的安装和配置 Tomcat 是在SUN公司的JSWDK(JavaServer Web DevelopmentKit)的基础上发展而来的一个优秀的Servlet容器,其本身完全是由Java编 ...

  3. SharePoint 更改管理帐户密码步骤

    // https://wenku.baidu.com/view/0fffab761ed9ad51f01df2df.html \

  4. Winsock编程基础2(UDP流程)

    UDP用户数据报协议 服务器端 <1 创建套接字(socket) <2 绑定IP地址和端口(bind) <3 收发数据(sendto, recvfrom) <4 关闭连接(cl ...

  5. 【错误解决】Intellj(IDEA) warning no artifacts configured

    : warning no artifacts configured,,上面木有Artifacts的选项,,好尴尬, [解决方案]artifacts,是maven中的概念(项目是maven项目),由于没 ...

  6. Java面试题整理:这些Java程序员面试中经常遇见的题目,必须掌握才能有好结果

    1.是否可以从一个static方法内部发出对非static方法的调用? 不可以.因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时 ...

  7. 快速理解Token,Cookie,Session

    在Web应用中,HTTP请求是无状态的.即:用户第一次发起请求,与服务器建立连接并登录成功后,为了避免每次打开一个页面都需要登录一下,就出现了cookie,Session. Cookie Cookie ...

  8. String补充

    String类不可变和可变字符序列区别_字符串比较 String类对象代表不可变的Unicode字符序列,因此我们可以将String对象称为“不可变对象”.也就是指对象内部成员变量的值无法再改变, p ...

  9. zabbix系列之九——添加钉钉告警

    一.添加钉钉机器人 1. 2. 复制webhook后面脚本用到:https://oapi.dingtalk.com/robot/send?access_token=36e69dd50bbcc54b7b ...

  10. C# Serialize

    一.序列化又称为串行化,是.NET运行时环境用来支持用户自定义类型的机制,目的是以某种存储给对象持久化,或者是将这种对象传输到另一个地方, 二. .NET框架提供了两种序列化的方式 一种是使用Bina ...