欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

BigDecimal详解

程序员文章站 2024-01-08 13:01:58
...

BigDecimal详解

引言

float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦。

	@Test
    public void test1(){
        /*
          0.30000000000000004
          0.19999999999999998
          0.020000000000000004
          2.9999999999999996
         */
        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);
    }

浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。如:2.4的二进制表示并非就是精确的2.4。反而最为接近的二进制表示是 2.3999999999999999。浮点数的值实际上是由一个特定的数学公式计算得到的。

常用方法

加减乘除

BigDecimal除法可能出现不能整除的情况比如 4.5/1.3,这时会报错* java.lang.ArithmeticException:* Non-terminating decimal expansion; no exact representable decimal result.
解决:
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)

public BigDecimal add(BigDecimal value);                        //加法
public BigDecimal subtract(BigDecimal value);                   //减法 
public BigDecimal multiply(BigDecimal value);                   //乘法
public BigDecimal divide(BigDecimal value);                     //除法


	@Test
    public void test5(){
        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));
        //public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
    }

BigDecimal比较大小

@Test
    public void test6(){
        BigDecimal a = new BigDecimal (101);
        BigDecimal b = new BigDecimal (111);

        //使用compareTo方法比较
        //注意:a、b均不能为null,否则会报空指针
        if(a.compareTo(b) < 0){
            System.out.println("a小于b");
        }

        if(a.compareTo(b) == 0){
            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");
        }
    }

BigDecimal转String

	/**
     *
     * BigDecimal转String
     * 10000000000
     * 100.000
     * 1E+2
     * 100
     */
	@Test
    public void test7(){
        // 浮点数的打印
        System.out.println(new BigDecimal("10000000000").toString());

        // 普通的数字字符串
        System.out.println(new BigDecimal("100.000").toString());

        // 去除末尾多余的0
        System.out.println(new BigDecimal("100.000").stripTrailingZeros().toString());

        //去除末尾多余的0 避免输出科学计数法
        System.out.println(new BigDecimal("100.000").stripTrailingZeros().toPlainString());
    }

	

RoundingMode

HALF_UP (四舍五入)

四舍五入

BigDecimal详解

HALF_DOWN(五舍六入)

BigDecimal详解

HALF_EVEN(四舍五入或者五舍六入)

被称为“银行家舍入”,并在美国主要用作朝着偶数目标舍入,有可能是四舍五入,也可能是五舍六入

BigDecimal详解

CEILING(向上取整)

正负数都是向数值最大化取整

BigDecimal详解

DOWN(向下取整)

正负数都是向数值最小化取整

BigDecimal详解

UNNECESSARY(化零为整)

看完感觉就是,需要舍掉的位数不是0,就报异常 ArithmeticException。如果需要舍掉的位数是0,就返回已结舍掉的正常结果。

BigDecimal详解

填坑

构造方法中不能是空字符串

new BigDecima("") 构造方法中不能是空字符串,否则会出现java.lang.NumberFormatException

	@Test
    public void test2(){
        BigDecimal decimal = new BigDecimal("");
        System.out.println(decimal);
    }

构造方法中最好使用字符串浮点数

如果是doeble类型,3.05–>3.04999999999999982236431605997495353221893310546875,这时需要保留位数,b1.setScale(1, RoundingMode.HALF_UP),但当使用四舍五入保留的时候,3.05 --> 3.0,显然有问题,
原因:
1、参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(3.05)所创建的BigDecimal正好等于3.05,但是它实际上等于3.04999999999999982236431605997495353221893310546875。这是因为3.05无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 3.05(虽然表面上等于该值)。
解决:

  • 手动把double转成字符串 Double.toString
  • 使用BigDecimal.valueOf(double d)
  • 使用BigDecimal(“3.05”)字符串表示
	@Test
    public void test3(){	
        double d = 3.05;
        BigDecimal b1 = new BigDecimal(d);
        System.out.println(b1);//3.04999999999999982236431605997495353221893310546875
        System.out.println(b1.setScale(1, RoundingMode.HALF_UP));//3.0
        String str = "3.05";
        BigDecimal b2 = new BigDecimal(str);
        System.out.println(b2);//3.05
        System.out.println(b2.setScale(1, RoundingMode.HALF_UP));//3.1
        BigDecimal b3 = BigDecimal.valueOf(d);
        System.out.println(b3);//3.05
        System.out.println(b3.setScale(1, RoundingMode.HALF_UP));//3.1
        System.out.println(b3.setScale(1, RoundingMode.HALF_EVEN));//3.1
    }

优先使用valueOf方法

从上面看,valueOf方法还是用到了字符串表示
原因:
String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法。

public static BigDecimal valueOf(double val) {
        // Reminder: a zero double returns '0.0', so we cannot fastpath
        // to use the constant ZERO.  This might be important enough to
        // justify a factory approach, a cache, or a few private
        // constants, later.
        return new BigDecimal(Double.toString(val));
    }

总结

  • 商业计算使用BigDecimal。
  • 尽量使用参数类型为String的构造函数。
  • BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。
相关标签: 杂记 java