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

  

原文地址:https://www.cnblogs.com/lyInfo/p/9129960.html