BigDecimal ROUND_HALF_UP 与 ROUND_HALF_DOWN 的区别

BigDecimal

首先先说一下自己遇到的问题

        System.out.println(new BigDecimal("111.1212335")
                           .setScale(6, BigDecimal.ROUND_HALF_UP));//111.121234
        System.out.println(new BigDecimal("111.1212335")
                           .setScale(6, BigDecimal.ROUND_HALF_DOWN));//111.121233

        System.out.println(new BigDecimal("111.12345678")
                           .setScale(4, BigDecimal.ROUND_HALF_UP));//111.1235
        System.out.println(new BigDecimal("111.12345678")
                           .setScale(4, BigDecimal.ROUND_HALF_DOWN));//111.1235
//      问题为啥这里的 ROUND_HALF_DOWN 小数点后第五位是5 为啥会给第四位进一??
   

ROUND_HALF_UP 与 ROUND_HALF_DOWN 的区别

先看看文档怎么解释的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJzzw74o-1610939903654)(BigDecimal.assets/image-20201225202538819.png)]

        
                  
          ROUND_HALF_UP
          若舍弃部分 (>=.5),则作   ROUND_UP   ;否则,作   ROUND_DOWN   。
        
          ROUND_HALF_DOWN
          若舍弃部分 ( > 5)  ROUND_UP;否则,作   ROUND_DOWN   。

 
          ROUND_UP
          总是在非   0   舍弃小数(即截断)之前增加数字。
          
           ROUND_DOWN
          从不在舍弃(即截断)的小数之前增加数字。

ROUND_UP 与 ROUND_DOWN 实例

//     ROUND_DOWN   舍入模式向零舍入  不管是0还是9都不加 相当于   截取
        double a4 = new BigDecimal("111.1934567").setScale(1,BigDecimal.ROUND_DOWN).doubleValue(); //111.1
        System.out.println(a4);
//      ROUND_UP  舍入模式从零开始  非 0舍弃小数加1
        double a5 = new BigDecimal("111.1034567").setScale(1,BigDecimal.ROUND_UP).doubleValue();//111.2
        double a6 = new BigDecimal("111.1934567").setScale(1,BigDecimal.ROUND_UP).doubleValue();//111.2
        System.out.println(a5);
        System.out.println(a6);
        System.out.println(new BigDecimal("111.10").setScale(1, BigDecimal.ROUND_UP));//111.1
        System.out.println(new BigDecimal("111.101").setScale(1, BigDecimal.ROUND_UP));//111.2

好了 看到这 我们再来看看 这个问题到底是为什么??

//      "111.121233 5" 这个例子保留六位小数,第六位之后开始 是.5  = .50 所以 up 进位, down 不进位
//      而111.12345678 这个例子保留四位小数 此数第四位后是.5678 > .50 所以两个都要进位
//		下面这个.51 > .50也一样 都会进位
        System.out.println(new BigDecimal("111.123451").setScale(4, BigDecimal.ROUND_HALF_UP));//111.1235
        System.out.println(new BigDecimal("111.123451").setScale(4, BigDecimal.ROUND_HALF_DOWN));//111.1235
截取出保留小数位数之后的数字(也就是舍弃部分)与 5  做比较 如果 (>=.5),则作   ROUND_UP  

下面我们整体了解一下BIgDecimal

引言

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

BIgDecimal的特点

    (1)商业计算使用BigDecimal。

    (2)尽量使用参数类型为String的构造函数。

    (3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保			存操作后的值。
               BigDecimal a = new BigDecimal("4.5");
               BigDecimal b = new BigDecimal("1.5");
               a.add(b);
               System.out.println(a);  //输出4.5. 加减乘除方法会返回一个新的BigDecimal对象,原来的a不变

    (4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。

BigDecimal 构造方法的参数类型问题

先看一下都有哪些构造方法?

​ BigDecimal一共有4个构造方法:

​ BigDecimal(int) 创建一个具有参数所指定整数值的对象。

​ BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用)

​ BigDecimal(long) 创建一个具有参数所指定长整数值的对象。

​ BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象 [1] 。

//看看这个例子
        BigDecimal bdTest = new BigDecimal(1.745);
        BigDecimal bdTest1 = new BigDecimal(0.745);
        bdTest = bdTest.setScale(2, BigDecimal.ROUND_HALF_UP);
        bdTest1 = bdTest1.setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println("bdTest:" + bdTest); // 1.75
        System.out.println("bdTest1:" + bdTest1); // 0.74

        System.out.println("=============================");
        System.out.println(new BigDecimal(1.745)); // 1.74500000000000010658141036401502788066864013671875
        System.out.println(new BigDecimal(0.745)); //0.74499999999999999555910790149937383830547332763671875

看看是什么原因

JDK的描述:

一方面,参数类型为double的构造方法的结果有一定的 **不可预知性** 。有人可能认为在Java中写入newBigDecimal(0.1)
        所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),
		但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。
        这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。
        这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

另一方面,String 构造方法是完全可预知的:写入 newBigDecimal("0.1") 将创建一个 BigDecimal,
       它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法。
        字符串不会丢失 使用参数为float或double的BigDecimal创建对象会丢失精度。
		因此强烈建议不要使用参数为float或double的BigDecimal创建对象。
       当double必须用作BigDecimal的源时,请使用Double.toString(double)转成String,
        然后使用String构造方法,或使用BigDecimal的静态方法valueOf,如下
        BigDecimal bDouble1 = BigDecimal.valueOf(2.3);
        BigDecimal bDouble2 = new BigDecimal(Double.toString(2.3));

        System.out.println("bDouble1=" + bDouble1);//2.3
        System.out.println("bDouble2=" + bDouble2); //2.3

BigDecimal 的加减乘除

        System.out.println("加减乘除:");


        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,3,BigDecimal.ROUND_HALF_UP));

这里有一点需要注意的是除法运算divide.

        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_DOWN
​ 从不在舍弃(即截断)的小数之前增加数字。
​ ROUND_UP
​ 总是在非 0 舍弃小数(即截断)之前增加数字。

​ ROUND_HALF_DOWN
​ 若舍弃部分> .5,则作 ROUND_UP;否则,作 ROUND_DOWN 。
​ ROUND_HALF_UP
​ 若舍弃部分>=.5,则作 ROUND_UP ;否则,作 ROUND_DOWN 。

​ ROUND_HALF_EVEN
​ 如果舍弃部分左边的数字为奇数,则作 ROUND_HALF_UP ;如果它为偶数,则作 ROUND_HALF_DOWN 。


​ ROUND_CEILING
​ 如果 BigDecimal 是正的,则做 ROUND_UP 操作;如果为负,则做 ROUND_DOWN 操作。
​ ROUND_FLOOR
​ 如果 BigDecimal 为正 则作 ROUND_DOWN , 如果为负,则作 。ROUND_UP

​ ROUND_UNNECESSARY
​ 该“伪舍入模式”实际是指明所要求的操作必须是精确的,,因此不需要舍入操作。

常用的 四舍五入 ROUND_HALF_UP 截取 ROUND_DOWN

double 或者float 固定小数位数:

 double v = new BigDecimal("0.745").setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
  BigDecimal bigDecimal =  new BigDecimal("0.745").setScale(2,BigDecimal.ROUND_HALF_UP);

文章参考

Java BigDecimal详解

public static void main(String[] args) {
    
    
        System.out.println("=====================");
//      如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
        System.out.println(new BigDecimal("111.12345557")
                           .setScale(6, BigDecimal.ROUND_HALF_EVEN));//111.123456

    }

猜你喜欢

转载自blog.csdn.net/weixin_44436598/article/details/112768029