zoukankan      html  css  js  c++  java
  • JAVA BigDecimal 详解 加减乘除 小数舍入模式

     java.math.BigDecimal

    • 概述

    Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。

    双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中.

    如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。

    ​BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/ 等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

    • BigDecimal常用构造函数
    1. BigDecimal(int)

      创建一个具有参数所指定整数值的对象

    2. BigDecimal(double)

      创建一个具有参数所指定双精度值的对象

    3. BigDecimal(long)

      创建一个具有参数所指定长整数值的对象

    4. BigDecimal(String)  常用

      创建一个具有参数所指定以字符串表示的数值的对象

    使用示例:

    
    
            BigDecimal a =new BigDecimal(0.1);
            System.out.println("a values is:"+a);
            System.out.println("=====================");
            BigDecimal b =new BigDecimal("0.1");
            System.out.println("b values is:"+b);
    
    
    

      

    
    

    结果示例:

    a values is:0.1000000000000000055511151231257827021181583404541015625
    =====================
    b values is:0.1
    

      

    原因分析:

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

    2)String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。

    3)当double必须用作BigDecimal的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用Double.toString(double)方法,然后使用BigDecimal(String)构造方法,将double转换为String。要获取该结果,请使用static valueOf(double)方法。

    • BigDecimal常用方法详解

    常用方法

    add(BigDecimal)
    
    BigDecimal对象中的值相加,返回BigDecimal对象
    
    subtract(BigDecimal)
    
    BigDecimal对象中的值相减,返回BigDecimal对象
    
    multiply(BigDecimal)
    
    BigDecimal对象中的值相乘,返回BigDecimal对象
    
    divide(BigDecimal)
    
    BigDecimal对象中的值相除,返回BigDecimal对象
    
    toString()
    
    将BigDecimal对象中的值转换成字符串
    
    doubleValue()
    
    将BigDecimal对象中的值转换成双精度数
    
    floatValue()
    
    将BigDecimal对象中的值转换成单精度数
    
    longValue()
    
    将BigDecimal对象中的值转换成长整数
    
    intValue()
    
    将BigDecimal对象中的值转换成整数
    

      

     除法特记:

      

    public BigDecimal divide( BigDecimal divisor, int scale, int roundingMode);

    divisor 除数, scale 精确小数位, roundingMode 舍入模式

      

    • BigDecimal的8种舍入模式

    
    
    BigDecimal resVal = new BigDecimal("0");
    
    
    
    

     舍入方法:setScale(0,BigDecimal.ROUND_UP) 第一个参数0表示舍入之后的小数位数,第二个参数表示舍入方式

    1、ROUND_UP 向上取整(正负远离零的舍入模式)
    
    
    BigDecimal bdNum = new BigDecimal("23.3693");  
    resVal = bdNum.setScale(0, BigDecimal.ROUND_UP);
    结果:24
    
    BigDecimal bdNum = new BigDecimal("-23.3693");  
    resVal = bdNum.setScale(0, BigDecimal.ROUND_UP);
    结果:-24


    2、ROUND_DOWN 向下取整(正负接近零的舍入模式)
    BigDecimal bdNum = new BigDecimal("23.3693");  
    resVal = bdNum.setScale(0, BigDecimal.ROUND_DOWN);
    结果:23
    
    BigDecimal bdNum = new BigDecimal("-23.3693");  
    resVal = bdNum.setScale(0, BigDecimal.ROUND_DOWN);
    结果:-23
    

    3、ROUND_CEILING 向大取整(比原数值大的方向) 如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同; 如果为负,则舍入行为与 ROUND_DOWN 相同。

    BigDecimal bdNum = new BigDecimal("23.3693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_CEILING);
    结果:24
    
    BigDecimal bdNum = new BigDecimal("-23.3693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_CEILING);
    结果:-23
    

    4、ROUND_FLOOR 向小取整(比原数值小的方向)如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同; 如果为负,则舍入行为与 ROUND_UP 相同。 

    BigDecimal bdNum = new BigDecimal("23.3693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_FLOOR);
    结果:23
    
    BigDecimal bdNum = new BigDecimal("-23.3693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_FLOOR);
    结果:-24
    

    5、ROUND_HALF_UP (四舍五入) 向“最接近的”数字舍入,如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。

    BigDecimal bdNum = new BigDecimal("23.3693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_UP);
    结果:23
    
    BigDecimal bdNum = new BigDecimal("23.6693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_UP);
    结果:24
    
    BigDecimal bdNum = new BigDecimal("23.5");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_UP);
    结果:24
    

    6、ROUND_HALF_DOWN (五舍六入) 向“最接近的”数字舍入,如果舍弃部分 <= 0.5,则舍入行为与 ROUND_DOWN 相同; 否则舍入行为与 ROUND_UP 相同。

    BigDecimal bdNum = new BigDecimal("23.3693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_DOWN);
    结果:23
    
    BigDecimal bdNum = new BigDecimal("23.6693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_DOWN);
    结果:24
    
    BigDecimal bdNum = new BigDecimal("23.5");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_DOWN);
    结果:23
    

    7、ROUND_HALF_EVEN

    向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。 如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同; 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。 
    四舍六入,五分两种情况。 如果前一位为奇数,则入位,否则舍去。
    BigDecimal bdNum = new BigDecimal("23.3693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_EVEN);
    结果:23
    
    BigDecimal bdNum = new BigDecimal("23.6693");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_EVEN);
    结果:24
    
    BigDecimal bdNum = new BigDecimal("23.5");// 如果与两个相邻数字的距离相等,则向相邻的偶数舍入
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_EVEN);
    结果:24  
    
    BigDecimal bdNum = new BigDecimal("22.5");// 如果与两个相邻数字的距离相等,则向相邻的偶数舍入
    resVal = bdNum.setScale(0, BigDecimal.ROUND_HALF_EVEN);
    结果:22 
    

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

    
    
    BigDecimal bdNum = new BigDecimal("22.3368");
    resVal = bdNum.setScale(4, BigDecimal.ROUND_UNNECESSARY);
    结果:22.3368
    
    BigDecimal bdNum = new BigDecimal("22.3368");
    resVal = bdNum.setScale(0, BigDecimal.ROUND_UNNECESSARY);
    结果:
      Exception in thread "main" java.lang.ArithmeticException: Rounding necessary
      at java.math.BigDecimal.commonNeedIncrement(BigDecimal.java:4148)
      at java.math.BigDecimal.needIncrement(BigDecimal.java:4204)
      at java.math.BigDecimal.divideAndRound(BigDecimal.java:4112)
      at java.math.BigDecimal.setScale(BigDecimal.java:2452)
      at com.example.demo622.Demo622ApplicationTests.main(Demo622ApplicationTests.java:213)
    

      

     

    BigDecimal 平方根

      

    BigDecimal bdNum1 = new BigDecimal(String.valueOf(Math.sqrt(4)));
    
      结果:2.0
    

    BigDecimal 幂次方

    BigDecimal res = new BigDecimal("3").pow(2); // 3的2次方
    
    结果: res = 9;
    

       

    BigDecimal大小比较

    java中对BigDecimal比较大小一般用的是bigdemical的compareTo方法

    BigDecimal a = new BigDecimal ("1");
    
    BigDecimal b = new BigDecimal ("2");
    
    int val = a.compareTo(b);

    System.out.println(val);

      返回结果分析:

    val = -1,表示a小于b;
    
    若 val = 0 ,表示a等于b;
    若 val = 1 ,表示a大于b;

    BigDecimal总结

    6.1、总结

    1. 在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
    2. 尽量使用参数类型为String的构造函数。
    3. BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。
    /**
     * BigDecimal高精度数学计算
     */
    public class ArithmeticUtils {
        //默认除法运算精度
        private static final int DEF_DIV_SCALE = 10;
    
        /**
         * 提供精确的加法运算
         *
         * @param v1 被加数
         * @param v2 加数
         * @return 两个参数的和
         */
        public static double add(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.add(b2).doubleValue();
        }
    
        /**
         * 提供精确的加法运算
         *
         * @param v1 被加数
         * @param v2 加数
         * @return 两个参数的和
         */
        public static BigDecimal add(String v1, String v2) {
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            return b1.add(b2);
        }
    
        /**
         * 提供精确的加法运算
         *
         * @param v1    被加数
         * @param v2    加数
         * @param scale 保留scale 位小数
         * @return 两个参数的和
         */
        public static String add(String v1, String v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException(
                        "The scale must be a positive integer or zero");
            }
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
        }
    
        /**
         * 提供精确的减法运算
         *
         * @param v1 被减数
         * @param v2 减数
         * @return 两个参数的差
         */
        public static double sub(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.subtract(b2).doubleValue();
        }
    
        /**
         * 提供精确的减法运算。
         *
         * @param v1 被减数
         * @param v2 减数
         * @return 两个参数的差
         */
        public static BigDecimal sub(String v1, String v2) {
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            return b1.subtract(b2);
        }
    
        /**
         * 提供精确的减法运算
         *
         * @param v1    被减数
         * @param v2    减数
         * @param scale 保留scale 位小数
         * @return 两个参数的差
         */
        public static String sub(String v1, String v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException(
                        "The scale must be a positive integer or zero");
            }
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
        }
    
        /**
         * 提供精确的乘法运算
         *
         * @param v1 被乘数
         * @param v2 乘数
         * @return 两个参数的积
         */
        public static double mul(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.multiply(b2).doubleValue();
        }
    
        /**
         * 提供精确的乘法运算
         *
         * @param v1 被乘数
         * @param v2 乘数
         * @return 两个参数的积
         */
        public static BigDecimal mul(String v1, String v2) {
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            return b1.multiply(b2);
        }
    
        /**
         * 提供精确的乘法运算
         *
         * @param v1    被乘数
         * @param v2    乘数
         * @param scale 保留scale 位小数
         * @return 两个参数的积
         */
        public static double mul(double v1, double v2, int scale) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return round(b1.multiply(b2).doubleValue(), scale);
        }
    
        /**
         * 提供精确的乘法运算
         *
         * @param v1    被乘数
         * @param v2    乘数
         * @param scale 保留scale 位小数
         * @return 两个参数的积
         */
        public static String mul(String v1, String v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException(
                        "The scale must be a positive integer or zero");
            }
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
        }
    
        /**
         * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
         * 小数点以后10位,以后的数字四舍五入
         *
         * @param v1 被除数
         * @param v2 除数
         * @return 两个参数的商
         */
    
        public static double div(double v1, double v2) {
            return div(v1, v2, DEF_DIV_SCALE);
        }
    
        /**
         * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
         * 定精度,以后的数字四舍五入
         *
         * @param v1    被除数
         * @param v2    除数
         * @param scale 表示表示需要精确到小数点以后几位。
         * @return 两个参数的商
         */
        public static double div(double v1, double v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException("The scale must be a positive integer or zero");
            }
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
    
        /**
         * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
         * 定精度,以后的数字四舍五入
         *
         * @param v1    被除数
         * @param v2    除数
         * @param scale 表示需要精确到小数点以后几位
         * @return 两个参数的商
         */
        public static String div(String v1, String v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException("The scale must be a positive integer or zero");
            }
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v1);
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
        }
    
        /**
         * 提供精确的小数位四舍五入处理
         *
         * @param v     需要四舍五入的数字
         * @param scale 小数点后保留几位
         * @return 四舍五入后的结果
         */
        public static double round(double v, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException("The scale must be a positive integer or zero");
            }
            BigDecimal b = new BigDecimal(Double.toString(v));
            return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
    
        /**
         * 提供精确的小数位四舍五入处理
         *
         * @param v     需要四舍五入的数字
         * @param scale 小数点后保留几位
         * @return 四舍五入后的结果
         */
        public static String round(String v, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException(
                        "The scale must be a positive integer or zero");
            }
            BigDecimal b = new BigDecimal(v);
            return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
        }
    
        /**
         * 取余数
         *
         * @param v1    被除数
         * @param v2    除数
         * @param scale 小数点后保留几位
         * @return 余数
         */
        public static String remainder(String v1, String v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException(
                        "The scale must be a positive integer or zero");
            }
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
        }
    
        /**
         * 取余数  BigDecimal
         *
         * @param v1    被除数
         * @param v2    除数
         * @param scale 小数点后保留几位
         * @return 余数
         */
        public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException(
                        "The scale must be a positive integer or zero");
            }
            return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
        }
    
        /**
         * 比较大小
         *
         * @param v1 被比较数
         * @param v2 比较数
         * @return 如果v1 大于v2 则 返回true 否则false
         */
        public static boolean compare(String v1, String v2) {
            BigDecimal b1 = new BigDecimal(v1);
            BigDecimal b2 = new BigDecimal(v2);
            int bj = b1.compareTo(b2);
            boolean res;
            if (bj > 0)
                res = true;
            else
                res = false;
            return res;
        }
    }
    

      

    https://www.cnblogs.com/zhangyinhua/p/11545305.html

    https://blog.csdn.net/haiyinshushe/article/details/82721234

  • 相关阅读:
    新建maven web工程报错
    Linux的系统suspend和resume
    Linux内核配置---menuconfig
    printk()函数学习笔记
    驱动调试笔记
    i2c接口笔记
    无线路由器的加密模式WEP,WPA-PSK(TKIP),WPA2-PSK(AES) WPA-PSK(TKIP)+WPA2-PSK(AES)。
    Linux下sniffer实现(转)
    驱动模块(3)——Linux交叉编译工具链
    ehci ohci 驱动逻辑
  • 原文地址:https://www.cnblogs.com/bors/p/bigdecimal.html
Copyright © 2011-2022 走看看