编程语言
首页 > 编程语言> > Java大数源码剖析(一) - BigInteger的底层数据结构

Java大数源码剖析(一) - BigInteger的底层数据结构

作者:互联网

本系列文章对Java大数类源码进行剖析, Java大数类包括java.math.BigInteger和java.math.BigDecimal, 本人使用的JDK版本为13.0.2

本系列文章按照以下顺序 :

BigInteger介绍

BigInteger是Java标准库提供的大整数类,提供对整数的各种操作和常用算法。与普通的int和Integer相比,BigInteger可以对大整数进行操作,而Java内置的整形最长的整形是long(64位有符号整数),一旦对需要多于64位的整数(说63位也许更好,因为有一位是符号位)进行处理时就需要BigInteger。

注意BigInteger是不可变的,也就是说,一旦创建就不能改变其数值。同时也决定了BigInteger提供的运算方法都是非本地算法。

注 : BigInteger所在包为java.math

BigInteger的主要操作

BigInteger以成员方法的形式提供和内置整型类似的操作, 除此之外还提供abs求绝对值、max/min求最大最小值、gcd求最大公约数······

本系列文章对以下方法的源码进行剖析:

BigInteger的底层数据结构

前文说过, 我剖析Java的大数库的最终目的是开发自己的基于C++的PGBigNumber库, 那么为什么我不研究现在已有的C++的大数库的源码呢?

这是因为, 我在github上找到的基于C++的大数库都是底层一个string, 每一个char就代表十进制的一个bit, 这显然是简单的, 但是同时也是低效的, CPU或者操作系统平台提供的对于32/64位整数的操作被利用的太少, 每次都操作十进制的一个bit, 对应于二进制的4个bit, 而利用内置的操作符依次就能至少能操作32个二进制位, 这是时间上的利用十进制存放大整数的劣势。

空间上,C++中每个char是一个byte,也就是说本来能存放8个二进制位的char仅仅被使用了4个bit,空间显然占用很多。并且,由于十进制的存储,二进制位操作显得十分不方便。

所以,我想到了利用二进制存放大整数,但是搜来搜去也没有找到有C++开源库采用这种方法,于是我去Java的源码中找了一下BigInteger的实现。很好!Java的BigInteger的底层正是利用二进制存放大整数。

**简单来说,**BigInteger的底层是利用一个int的数组(称为mag)存储大整数,也就是说,当要存放的整数大于32位时,就会被分割成32位为一组的形式,每一组就作为底层数组的一个元素。并且,BigInteger的底层数组mag时大端存放的,也就是说mag[0]、mag[1]…mag[mag.length - 1]分别代表整数的最高32位、次高32位…最低32位。

**并且,**BigInteger的mag数组仅仅用来存放绝对值的二进制位,其符号被signum存放,signum为0即代表0;signum为1即代表正数、signum为-1即代表负数。

为什么不直接按照补码存放呢? 我认为,是为了使操作的算法更为高效和方便。

BigInteger的成员变量/常量

final int signum; // 符号标志, -1 : 负数, 0 : 零, 1 : 正数

final int[] mag;  // 存放大整数二进制位的数组, 大端存储

/*
 ** 下面的变量都是为了懒求值而设置的, 暂时不用管.
 *  所谓懒求值是指当用到的时候才求值;
 *
 ** 而求过之后由于BigInteger是不可变的, 
 *  这些所求也不会变, 所以就利用变量存下,
 *  当第二次求这些的时候, 直接返回即可;
 *
 ** 之所以存放这些值+1或+2(plusOne plusTwo),
 *  是为了方便判断是否被求过.
 */
private int bitCountPlusOne;
private int bitLengthPlusOne;
private int lowestSetBitPlusTwo;
private int firstNonzeroIntNumPlusTwo;

下一篇文章 : Java大数源码剖析(二) - BigInteger的加减操作

标签:BigInteger,Java,整数,源码,存放,mag
来源: https://blog.csdn.net/PGZXB/article/details/118765836