BigDecimal类学习笔记

float类型和double类型精度失真

float类型和double类型在高精度场景下会出现精度失真的情况。

例如:

System.out.println(0.99999999f);  // 八个9

输出:

1.0

 

这就涉及浮点数在计算机中的表示方式。

浮点数的表示方式:

S:符号位(01负)

M尾数(小数点后面的数)

E阶码(指数)

 

 

 

阶码(指数):

  float类型:8位,表示范围为-127 ~ 128

  double类型:11位,表示范围为-1023 ~ 1024

 

尾数部分:

  float类型:23位,十进制2^23=8388608,所以十进制精度只有6 ~ 7位;

  double类型:52位,十进制就是 2^52 = 4503599627370496,所以十进制精度只有15 ~ 16位。

 

浮点数二进制表示实例:(转换工具:http://binaryconvert.com/convert_float.html

 

-0.5转化为二进制数为:-0.1

S=1, M=0, E=-1

进行二进制浮点数表示时,E需加127(即为126)(二进制:01111110

所以-0.5f二进制浮点数表示为:

10111111 00000000 00000000 00000000

 

来看1.0f0.9999999f的浮点数二进制表示:

1.0转化为二进制数为:1.0

S=0, M=0, E=0

进行二进制浮点数表示时,E需加127(二进制:01111111

所以1.0f二进制浮点数表示为:

00111111 10000000 00000000 00000000

 

同理,0.99999999f二进制浮点数表示为:

00111111 10000000 00000000 00000000

 

显然,在计算机中1.0f0.999999999f的浮点数表示是一致的。

在金融领域这样的精度失真将是致命的。Java提供了BigDecimal类实现高精度计算。

 

BigDecimal

  BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负scale 次幂。因此,BigDecimal表示的数值是(unscaledValue×10-scale)。BigDecimal对象表示的是不可变的,任意精度的有符号十进制数

构造器

例:

BigDecimal num = new BigDecimal(0.1);   // 利用double型构造
System.out.println(num);
// 0.1000000000000000055511151231257827021181583404541015625

BigDecimal num = new BigDecimal("0.1"); // 利用String构造
System.out.println(num);                // 0.1

// 利用数值型进行构造会产生不可预见性,通常使用字符串构造。

char[] arr = {'1','2','3','4','5','6'};
BigDecimal num = new BigDecimal(arr,1,4); // 使用指定字符数组,指定索引及长度构造
System.out.println(num);  // 2345

 

BigDecimal中的RoundingMode

舍入规则

例:

     BigDecimal positive_num = new BigDecimal("0.145");
        BigDecimal negative_num = new BigDecimal("-0.145");
        System.out.println(positive_num.setScale(2,RoundingMode.CEILING));  // 0.15 向正无穷大的方向舍入(Math.round即为此模式)
        System.out.println(negative_num.setScale(2,RoundingMode.CEILING));  // -0.14 向正无穷大的方向舍入

        System.out.println(positive_num.setScale(2,RoundingMode.FLOOR));    // 0.14 向负无穷大的方向舍入
        System.out.println(negative_num.setScale(2,RoundingMode.FLOOR));    // -0.15 向负无穷大的方向舍入

        System.out.println(positive_num.setScale(2,RoundingMode.UP));       // 0.15 向远离零的方向舍入
        System.out.println(negative_num.setScale(2,RoundingMode.UP));       // -0.15 向远离零的方向舍入

        System.out.println(positive_num.setScale(2,RoundingMode.DOWN));     // 0.14 向接近零的方向舍入
        System.out.println(negative_num.setScale(2,RoundingMode.DOWN));     // -0.14 向接近零的方向舍入

        System.out.println(positive_num.setScale(2,RoundingMode.HALF_UP));  // 0.15 四舍五入模式
        System.out.println(negative_num.setScale(2,RoundingMode.HALF_UP));  // -0.15 四舍五入模式

        System.out.println(positive_num.setScale(2,RoundingMode.HALF_DOWN));// 0.14 五舍六入模式
        System.out.println(negative_num.setScale(2,RoundingMode.HALF_DOWN));// -0.14 五舍六入模式
        

        /**
         * 银行家舍入(Banker's Round),金融领域尽量使用此舍入模式
         *  舍去位小于5,直接舍去;
         *  舍去位大于等于6,进位后舍去;
         *  舍去位等于5时:
         *          5后有非0数字,进位后舍去
         *          5后面是0,则5的前一位是奇数进位,偶数舍去
         */
        BigDecimal num1 = new BigDecimal("0.254");  // 舍去位小于5
        System.out.println(num1.setScale(2,RoundingMode.HALF_EVEN)); // 0.25

        BigDecimal num2 = new BigDecimal("0.256");  // 舍去位大于等于6
        System.out.println(num2.setScale(2,RoundingMode.HALF_EVEN)); // 0.26

        BigDecimal num3 = new BigDecimal("0.2551");  // 舍去位等于5,5后面非0
        System.out.println(num3.setScale(2,RoundingMode.HALF_EVEN)); // 0.26

        BigDecimal num4 = new BigDecimal("0.255");  // 舍去位等于5,5后面是0,5的前一位是奇数进位
        System.out.println(num4.setScale(2,RoundingMode.HALF_EVEN)); // 0.26

 

BigDecimal常用方法的使用

例:

        BigDecimal num1 = new BigDecimal("125");
        BigDecimal num2 = new BigDecimal("4");
        System.out.println(num1.add(num2));    // 加法
        System.out.println(num1.divide(num2)); // 除法
        System.out.println(num1.divide(num2, RoundingMode.CEILING)); // 指定舍入规则的除法
        System.out.println(num1.divide(num2, 1,RoundingMode.CEILING)); // 指定小数位数和舍入规则的除法
        System.out.println(num1.divideToIntegralValue(num2)); // 除法只保留整数部分
        System.out.println(num1.remainder(num2));  // 除法只保留余数
        BigDecimal[] result = num1.divideAndRemainder(num2); // 带余数除法
        for (BigDecimal bigDecimal : result) {
            System.out.println(bigDecimal.toString());
        }
        System.out.println(num1.pow(2));  // 次方
        System.out.println(num1.subtract(num2)); // 减法
        System.out.println(num1.multiply(num2)); // 乘法

        BigDecimal num3 = new BigDecimal("-2");
        System.out.println(num3.abs());   // 绝对值

        System.out.println(num1.compareTo(num2)); // num1>num2返回1;num1>num2返回-1;num1==num2返回0

        byte i = num1.byteValueExact();  // 转化为byte类型,-128<=num1<=127,否则抛异常
        System.out.println(i);

        System.out.println(num1.byteValue()); // 转化为byte类型,越界值出错,不抛异常

        int int_value = num1.intValue();   // 转化为int类型
        System.out.println(int_value);

        float float_value = num1.floatValue(); // 转化为float类型
        System.out.println(float_value);

        double double_value = num1.doubleValue(); // 转化为double类型
        System.out.println(double_value);

        long long_value = num1.longValue();  // 转化为long类型
        System.out.println(long_value);

        System.out.println(num1.max(num2));  // 返回两者最大值

        System.out.println(num1.min(num2)); // 返回两者最小值

        BigDecimal num4 = new BigDecimal("12.345");
        System.out.println(num4.movePointLeft(1)); // 1.2345 小数点左移1位
        System.out.println(num4.movePointLeft(-1)); // 123.45 小数点右移1位

        System.out.println(num4.movePointRight(1)); // 123.45 小数点右移1位
        System.out.println(num4.movePointRight(-1)); // 1.2345 小数点左移1位

        System.out.println(num4.negate());  // 返回"-num"

        System.out.println(num4.plus());  // 返回"+num"

        System.out.println(num4.precision()); // 5 返回精度值

        System.out.println(num4.scale());  // 3 返回小数位数

        System.out.println(num4.scaleByPowerOfTen(2)); // 乘以10^2

        System.out.println(num4.setScale(2,RoundingMode.UP));  // 指定舍入模式设置小数点位数

        System.out.println(num4.signum()); // 判断符号,正数返回1,负数返回-1

        System.out.println(num4.ulp()); // 0.001 0值返回1,其他返回精确到的最小单位

        System.out.println(num4.unscaledValue()); // 返回乘以10^this.scale的BigInteger,相当于去掉小数点

        System.out.println(num4.stripTrailingZeros()); // 去除尾部无效的0

        System.out.println(num4.toBigInteger());  // 返回BigInteger(小数部分被丢弃)

        System.out.println(num4.toEngineeringString()); // 返回工程计数字符串

        BigDecimal num5 = new BigDecimal("0.1234");
        BigDecimal num6 = num5.pow(32);
        System.out.println(num6.toString());    // 适当的时候会进行计数法表示的字符串
        System.out.println(num6.toPlainString()); // 不修饰本色显示字符串

        /**
         * 格式化
         */
        BigDecimal num7 = new BigDecimal("0.1234");

        NumberFormat currency = NumberFormat.getCurrencyInstance(); // 货币格式化引用
        NumberFormat percent = NumberFormat.getPercentInstance();  // 百分比格式化引用
        percent.setMaximumFractionDigits(2); // 百分比小数点后最多2位

        System.out.println(currency.format(num7));  // ¥0.12
        System.out.println(percent.format(num7));  // 12.34%

 

posted @ 2020-05-06 17:13  Jiazhongxin  阅读(384)  评论(0编辑  收藏  举报