Java中BigDecimal类型的加减乘除及大小比对

Java中BigDecimal类型的加减乘除及大小比对

    • 前言
    • BigDecimal解释
    • 创建BigDecimal类型
    • 加减乘除计算
    • 结果分析
    • 结论
    • 除法的补充
    • 两个BigDecimal的比对

前言

在使用Java语言进行商业计算的时候都是使用的java.math.BigDecimal,其中主要原因为浮点数类型floatdouble运算会丢失精度。

package action;

public class TestBigDecimal {
	
	public static void main(String[] args) {
	    System.out.println(0.05f + 0.03);
	    System.out.println(1.13f - 0.62);
	    System.out.println(2.021f * 100);
	    System.out.println(202.1f / 100);
	    System.out.println(Math.round(4.015f * 100) / 100.0);// 4.01 四舍五入保留两位
	}

}

运行结果
Java中BigDecimal类型的加减乘除及大小比对_第1张图片
从运行结果可以看出,浮点型在进行计算的时候会丢失精度,在商业计算中是不被允许的。

BigDecimal解释

由任意精度的整数非标度值(unscaledValue)和32位的整数标度(scale)组成。其值为该数的非标度值乘以10的负scale次幂,即为(unscaledValue * 10-scale),是不可变的、任意精度的有符号十进制数。

创建BigDecimal类型

在使用BigDecimal时常用的两个构造函数有两个,一个是用String,另一个使用double
和直接使用浮点型计算一样,在使用构造函数转换为BigDecimal类型的时候也会存在计算精度降低的情况。

public BigDecimal(String str);
public BigDecimal(double dob);

加减乘除计算

  • 加法:add()函数
  • 减法:subtract()函数
  • 乘法:multiply()函数
  • 除法:divide()函数
  • 绝对值:abs()函数

首先验证不同的构造方法计算带来的精度问题。

package action;

import java.math.BigDecimal;

public class TestBigDecimal {
	
	public static void main(String[] args) {
		/*
		 * System.out.println(0.05f + 0.03); System.out.println(1.13f - 0.62);
		 * System.out.println(2.021f * 100); System.out.println(202.1f / 100);
		 * System.out.println(Math.round(4.015f * 100) / 100.0);// 4.01 四舍五入保留两位
		 */	    
	    
	    
		//使用浮点型构造
	    BigDecimal dob1 = new BigDecimal(0.005);
        BigDecimal dob2 = new BigDecimal(1000000);
        BigDecimal dob3 = new BigDecimal(-1000000);
        
        //使用字符串构造
        BigDecimal str1 = new BigDecimal("0.005");
        BigDecimal str2 = new BigDecimal("1000000");
        BigDecimal str3 = new BigDecimal("-1000000");
        
        //进行加法
        BigDecimal addDob = dob1.add(dob2);
        System.out.println("加法用value结果:" + addDob);
        BigDecimal addStr = str1.add(str2);
        System.out.println("加法用string结果:" + addStr);
        
        //进行减法
        BigDecimal subtractDob = dob2.subtract(dob1);
        System.out.println("减法用value结果:" + subtractDob);
        BigDecimal subtractStr = str2.subtract(str1);
        System.out.println("减法用string结果:" + subtractStr);
        
        //进行乘法
        BigDecimal multiplyDob = dob1.multiply(dob2);
        System.out.println("乘法用value结果:" + multiplyDob);
        BigDecimal multiplyStr = str1.multiply(str2);
        System.out.println("乘法用string结果:" + multiplyStr);
        
        //进行除法
        BigDecimal divideDob = dob2.divide(dob1,20, BigDecimal.ROUND_HALF_UP);
        System.out.println("除法用value结果:" + divideDob);
        BigDecimal divideStr = str2.divide(str1,20, BigDecimal.ROUND_HALF_UP);
        System.out.println("除法用string结果:" + divideStr);
	    
        //绝对值
        BigDecimal absDob = dob3.abs();
        System.out.println("绝对值用value结果:" + absDob);
        BigDecimal absStr = str3.abs();
        System.out.println("绝对值用string结果:" + absStr);
	}

}

执行结果:
Java中BigDecimal类型的加减乘除及大小比对_第2张图片

结果分析

1、System.out.println()中的数字的展示默认是使用double类型的,但double类型小数部分计算是不精准。
2、在使用double类型进行BigDecimal类构造时,计算的结果也是不精确的。
因为不是所有的浮点数都能够被精确的表示成一个double 类型值,因此它会被表示成与它最接近的 double 类型的值。

结论

在构造BigDecimal类的时候必须使用String才能够保证高精度的计算结果。

除法的补充

在使用除法divide()的时候,必须带入相关参数,首先看一下divide()方法的源码。

/**
     * Returns a {@code BigDecimal} whose value is {@code (this /
     * divisor)}, and whose scale is as specified.  If rounding must
     * be performed to generate a result with the specified scale, the
     * specified rounding mode is applied.
     *
     * 

The new {@link #divide(BigDecimal, int, RoundingMode)} method * should be used in preference to this legacy method. * * @param divisor value by which this {@code BigDecimal} is to be divided. * @param scale scale of the {@code BigDecimal} quotient to be returned. * @param roundingMode rounding mode to apply. * @return {@code this / divisor} * @throws ArithmeticException if {@code divisor} is zero, * {@code roundingMode==ROUND_UNNECESSARY} and * the specified scale is insufficient to represent the result * of the division exactly. * @throws IllegalArgumentException if {@code roundingMode} does not * represent a valid rounding mode. * @see #ROUND_UP * @see #ROUND_DOWN * @see #ROUND_CEILING * @see #ROUND_FLOOR * @see #ROUND_HALF_UP * @see #ROUND_HALF_DOWN * @see #ROUND_HALF_EVEN * @see #ROUND_UNNECESSARY */ public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) { if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY) throw new IllegalArgumentException("Invalid rounding mode"); if (this.intCompact != INFLATED) { if ((divisor.intCompact != INFLATED)) { return divide(this.intCompact, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode); } else { return divide(this.intCompact, this.scale, divisor.intVal, divisor.scale, scale, roundingMode); } } else { if ((divisor.intCompact != INFLATED)) { return divide(this.intVal, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode); } else { return divide(this.intVal, this.scale, divisor.intVal, divisor.scale, scale, roundingMode); } } }

其中:divide(BigDecimal divisor, int scale, int roundingMode)的参数分别代表divisor(除数),scale(精确小数位数),roundingMode(舍入模式)。
另:舍入模式主要有8中如下:
1、ROUND_UP
舍入远离零的舍入模式。在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。注意,此舍入模式始终不会减少计算值的大小。
2、ROUND_DOWN
接近零的舍入模式。在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。注意,此舍入模式始终不会增加计算值的大小。

3、ROUND_CEILING
接近正无穷大的舍入模式。如果 BigDecimal为正,则舍入行为与 ROUND_UP 相同;如果为负,则舍入行为与 ROUND_DOWN相同。注意,此舍入模式始终不会减少计算值。
4、ROUND_FLOOR
接近负无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与ROUND_DOWN 相同;如果为负,则舍入行为与 ROUND_UP相同。注意,此舍入模式始终不会增加计算值。

5、ROUND_HALF_UP
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。注意,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。

6、ROUND_HALF_DOWN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN相同(五舍六入)。

7、ROUND_HALF_EVEN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。如果前一位为奇数,则入位,否则舍去。

以下例子为保留小数点1位,那么这种舍入方式下的结果:1.15=>1.2、1.25=>1.2

8、ROUND_UNNECESSARY
断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException

两个BigDecimal的比对

使用compareTo()方法进行比较。
该方法用于两个相同数据类型的比较,两个不同类型的数据不能用此方法来比较。

/*
参数
referenceName -- 可以是一个 Byte, Double, Integer, Float, Long 或 Short 类型的参数。
返回值
如果指定的数与参数相等返回0。
如果指定的数小于参数返回 -1。
如果指定的数大于参数返回 1。
*/

//语法
public int compareTo( NumberSubClass referenceName )
//对上面的计算值比对大小
System.out.println(addDob.compareTo(addStr)); //1
System.out.println(subtractDob.compareTo(subtractStr)); //-1
System.out.println(absDob.compareTo(absStr));  //0

//结果:1、-1、0 
//表示addDob大于addStr,subtractDob小于subtractStr,absDob等于absStr

你可能感兴趣的:(Java学习,java,BigDecimal)