zoukankan      html  css  js  c++  java
  • 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%
  • 相关阅读:
    java面试系列<1>——java基础
    Netty 面试必备知识点
    Redis 面试必备知识点
    MySQL 面试必备知识点
    肝了一个半月的 Java 项目快速开发脚手架:Chewing
    线上BUG:MySQL死锁分析实战
    2021-07-01阅读小笔记:Spring ioc 之组件扫描
    2021-06-13:G1垃圾回收器
    Dubbo原理剖析 之 @DubboReference.version设置为*
    2021-04-09阅读小笔记:JDK8中的一等公民和Stream小概念
  • 原文地址:https://www.cnblogs.com/jiazhongxin/p/12837564.html
Copyright © 2011-2022 走看看