- 为什么要使用BigDecimal
- 构造方法
- 加减乘除
- 舍入模式(对BigDecimal进行截取)
- 取余数
- 比较大小
- BigDecimal 转 String
- BigDecimal与double和int转换
- 格式化显示 BigDecimal
- 使用DecimalFormat
- 使用String.format()
- 总结
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_UP
a = 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)
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?