• BigDecimal 和 BigInteger 介绍


    一、BigDecimal

      熟悉浮点记数的朋友知道,用二进制表示十进制小数是存在误差的,在涉及到金钱等其他对小数精度要求高的场景下Java提供了BigDecimal类,以满足需要。

      先看构造函数: 

        public static void main(String[] args) {
            BigDecimal aNum = new BigDecimal(0.11D); // double
            BigDecimal bNum = new BigDecimal("1.015"); // String
            BigDecimal cNum = new BigDecimal("1.15".toCharArray()); // char[]
            BigDecimal dNum = new BigDecimal(100L);// int or long
            System.out.println(aNum);
            System.out.println(bNum);
            System.out.println(cNum);
            System.out.println(dNum);
            System.out.println(aNum.setScale(2, BigDecimal.ROUND_HALF_UP));
        }

      可以看到一共可满足四大类初始化的方法,即整数、浮点数、字符串、和字符数组。运行后得到如下输出:

    0.11000000000000000055511151231257827021181583404541015625
    1.015
    1.15
    100
    0.11

     输出中浮点并不能精确初始化小数,要得到预想的输出,即0.11,可以在使用时设置精度。如代码最后一行

        BigDecimal setScale(int newScale, int roundingMode)

     此函数不仅可用于输出格式化,更多的是在加减乘除运算后,设定结果的精度。另外如果想精确初始化小数,建议采用入参为字符串的构造函数。讲到这里,再介绍以下几种常见的近似方式:

      1、BigDecimal.ROUND_DOWN 向下取整,例如:1.119 ->1.11;
      2、BigDecimal.ROUND_UP 向上取整,例如:1.111 -> 1.12;
      3、BigDecimal.ROUND_HALF_DOWN 和 BigDecimal.ROUND_HALF_UP,二者都是以0.5为分界点向上或向下取整近似,ROUND_HALF_DOWN在近似值为0.5时取整为0,ROUND_HALF_UP则取整为1。

     另外想说,以0.115D测试区别时,BigDecimal.ROUND_HALF_DOWN 和 BigDecimal.ROUND_HALF_UP两个,近似结果都是0.12,原因是浮点在二进制转化时的误差问题,将0.115D打出来实际是:0.11500000000000000499600361081320443190634250640869140625,按2位近似后面部分实际上是大于0.005的,所以Up和Down都选择了向上进位。要比较这2个区别可以使用参数为String的构造函数得到精确小数。

     JDK中自带了加减乘除的方法:

    public BigDecimal add(BigDecimal value);  
    public BigDecimal subtract(BigDecimal value);  
    public BigDecimal multiply(BigDecimal value);  
    public BigDecimal divide(BigDecimal value);  

    二、BigInteger 

      之所以关注到BigInteger是因为在做某个阶乘运算时,发现java中基本类型long在算到18!就GG了,于是发现了这个神奇的东西。

      先谈初始化,奇怪的是没有直接提供参数为整数的构造函数而是以静态函数BigInteger.valueOf()的形式,在String类型的参数初始化时又是以构造函数的形式,不支持valueOf(),具体如下:

    BigInteger eNum = BigInteger.valueOf(11L); 
    BigInteger fNum = new BigInteger("123414");

      同样提供基本运算方法:

    1、 add(b);//相加
    2、subtract(); //相减
    3、multiply(); //相乘
    4、divide(); //相除取整
    5、remainder(); //取余
    6.pow(); //求幂次方
    7.gcd(); //最大公约数
    8.abs(); //绝对值
    9.negate(); //取反数

      BigInteger如何存储大数字?

      阅读源码可以发现,输入值val最终是被存在mag中的,而mag初始定义为:final int[] mag,也就说BigInteger存储数字的基本方向就是用int[]数组分位存储。而int最大值为2147483647,数组下标默认为int型,即mag数组最大能有2147483647个元素,每个元素最大表示2147483647,因此BigIntrger能表示的最大值为[-2^(2147483647*32-1) ,2^(2147483647*32-1)-1]。

        public BigInteger(byte[] val) {
            if (val.length == 0)
                throw new NumberFormatException("Zero length BigInteger");
    
            if (val[0] < 0) {
                mag = makePositive(val);
                signum = -1;
            } else {
                mag = stripLeadingZeroBytes(val);
                signum = (mag.length == 0 ? 0 : 1);
            }
            if (mag.length >= MAX_MAG_LENGTH) {
                checkRange();
            }
        }

      另外,为了提高效率在-16~16范围内的BigInteger返回的是同一个实例,这个似乎和Integer在-128到 127设置静态缓存是一样的做法。

         public static BigInteger valueOf(long val) {
            // If -MAX_CONSTANT < val < MAX_CONSTANT, return stashed constant
            if (val == 0)
                return ZERO;
            if (val > 0 && val <= MAX_CONSTANT)
                return posConst[(int) val];
            else if (val < 0 && val >= -MAX_CONSTANT)
                return negConst[(int) -val];
    
            return new BigInteger(val);
        }

    参考:

      1、https://blog.csdn.net/gjb724332682/article/details/51488765

      

  • 相关阅读:
    8.池化内存分配
    7.netty内存管理-ByteBuf
    6.ChannelPipeline
    5.接入客户端连接
    4.Netty执行IO事件和非IO任务
    3.NioEventLoop的启动和执行
    2.NioEventLoop的创建
    1.netty服务端的创建
    微信订阅号开发初探
    jmeter自动化脚本编写
  • 原文地址:https://www.cnblogs.com/lyInfo/p/9129960.html
Copyright © 2020-2023  润新知