1 public BigDecimal(char[] in, int offset, int len, MathContext mc) {// 使用字符数组的构造方法,一般我们推荐使用的是一String类为参数的构造以保证精度不会丢失
2 // protect against huge length.
3 if (offset + len > in.length || offset < 0)// 索引偏移量不能为负数且索引偏移量与使用的字符数之和不能超过字符数组的长度,否则抛出数字格式化异常
4 throw new NumberFormatException("Bad offset or len arguments for char[] input.");
5 // This is the primary string to BigDecimal constructor; all
6 // incoming strings end up here; it uses explicit (inline)
7 // parsing for speed and generates at most one intermediate
8 // (temporary) object (a char[] array) for non-compact case.
9
10 // Use locals for all fields values until completion
11 int prec = 0; // record precision value
12 int scl = 0; // record scale value
13 long rs = 0; // the compact value in long
14 BigInteger rb = null; // the inflated value in BigInteger,以上定义了4个变量用于最后对成员变量赋值使用,若值未改变则世界使用当前默认值
15 // use array bounds checking to handle too-long, len == 0,
16 // bad offset, etc.
17 try {
18 // handle the sign,首先处理第一个字符,因其可能为符号位需要特殊处理
19 boolean isneg = false; // assume positive,先假定为正数,因此定义一个标志位isneg为false
20 if (in[offset] == '-') {
21 isneg = true; // leading minus means negative
22 offset++;
23 len--;// 若第一个字符为'-'号,表明该数值为负数,标志位isneg为true,同时将索引偏移量自增,将需要的字符个数自减
24 } else if (in[offset] == '+') { // leading + allowed
25 offset++;
26 len--;// 若第一个字符为'+'号,则不需要改变标志位,将索引偏移量自增,将需要的字符个数自减
27 }
28
29 // should now be at numeric part of the significand,接下来处理数组的数值部分字符
30 boolean dot = false; // true when there is a '.',首先定义小数点标志位dot,默认为false
31 long exp = 0; // exponent,定义指数值exp为0
32 char c; // current character,定义当前字符c,用于循环使用
33 boolean isCompact = (len <= MAX_COMPACT_DIGITS);// MAX_COMPACT_DIGITS为常量18,用于判断最终数值的数字位数,若小于等于18,则该数值的数值部分(不考虑小数点)可以使用long类型表示
34 // integer significand array & idx is the index to it. The array
35 // is ONLY used when we can't use a compact representation.
36 int idx = 0;
37 if (isCompact) {// len小于等于18
38 // First compact case, we need not to preserve the character
39 // and we can just compute the value in place.
40 for (; len > 0; offset++, len--) {// 开启循环取出字符数组中的数值
41 c = in[offset];// c代表当前字符
42 if ((c == '0')) { // c为'0'
43 if (prec == 0)// 若有效数位为0,则赋值为1(刚开始觉得这里有问题,如果第一个字符就是0则有效位数不应该自增才对,这里其实与第二个if语句体对应,且往下看)
44 prec = 1;
45 else if (rs != 0) {// 若有效数位不为0,rs也为0,将rs变为原值的10倍(即上升一个进位制),并将有效数位自增
46 rs *= 10;
47 ++prec;
48 } // else digit is a redundant leading zero
49 if (dot)// 若之前循环出现过字符'.'也就是小数点,则将有效小数位自增
50 ++scl;
51 } else if ((c >= '1' && c <= '9')) { // 若c为数值1~9,计算字符的实际映射的数值并赋值给digit变量
52 int digit = c - '0';
53 if (prec != 1 || rs != 0)// 不执行此步骤出现的情况:字符数组前几位均为字符'0',此时出现第一个数值字符时有效数值位数不需要改变(因为在这种情况下,在第一次出现'0'时已经提前增加了有效数位prec的值)
54 ++prec; // prec unchanged if preceded by 0s,这一个与上边的疑问对应,若在第一个数值字符出现之前有多个'0'字符出现,则有效位数一定为1,此时当前数值字符出现时我们不改变prec的值
55 rs = rs * 10 + digit;// 十进制计算数值
56 if (dot)
57 ++scl;
58 } else if (c == '.') { // 当前字符为'.'小数点
59 // have dot
60 if (dot) // two dots,之前循环已经存在一个小数点则目前有两个则抛出异常
61 throw new NumberFormatException();
62 dot = true;
63 } else if (Character.isDigit(c)) { // 字符是其他类型的数字(Unicode编码的非阿拉伯数字)
64 int digit = Character.digit(c, 10);// 获取字符所映射的数值,一下步骤与上面的分析是一样的
65 if (digit == 0) {
66 if (prec == 0)
67 prec = 1;
68 else if (rs != 0) {
69 rs *= 10;
70 ++prec;
71 } // else digit is a redundant leading zero
72 } else {
73 if (prec != 1 || rs != 0)
74 ++prec; // prec unchanged if preceded by 0s
75 rs = rs * 10 + digit;
76 }
77 if (dot)
78 ++scl;
79 } else if ((c == 'e') || (c == 'E')) {// 若当前字符是'e'或'E',则代表科学计数法
80 exp = parseExp(in, offset, len);// 解析字符'e'或'E'之后的指数值后,若通过校验则结束循环
81 // Next test is required for backwards compatibility
82 if ((int) exp != exp) // overflow,溢出(已经超出int的表数范围)则抛出格式化异常,一般指数也不会这么大int可以大概表数正负21亿
83 throw new NumberFormatException();
84 break; // [saves a test]
85 } else {// 当前字符不是数值或者指数标识符抛出异常
86 throw new NumberFormatException();
87 }
88 }
89 if (prec == 0) // no digits found,循环结束后若一个数值(包括'0')也没找到则抛出格式化异常,
90 throw new NumberFormatException();
91 // Adjust scale if exp is not zero.
92 if (exp != 0) { // had significant exponent,科学计数法表达式指数不为0则需要调整有效小数位数
93 scl = adjustScale(scl, exp);// 特别的有效小数位数可以为负数
94 }
95 rs = isneg ? -rs : rs;// 应用数值符号标志位恢复正负
96 int mcp = mc.precision;// 获取MathContext上下文的有效位数
97 int drop = prec - mcp; // prec has range [1, MAX_INT], mcp has range [0, MAX_INT];
98 // therefore, this subtract cannot overflow
99 if (mcp > 0 && drop > 0) { // do rounding,若上下文的有效位数>0,且解析出的数值的有效位数大于上下文的有效位数则需要舍入
100 while (drop > 0) {
101 scl = checkScaleNonZero((long) scl - drop);// 根据原有效小数位数与需要舍去的位数对有效小数位数进行修正
102 rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);// 根据舍入模式与需要舍入的位数对rs进行舍入处理
103 prec = longDigitLength(rs);// longDigitLength()方法获取long类型数值rs的有效数字位数
104 drop = prec - mcp;// 重新计算需要舍去的位数,若大于0则继续循环
105 }
106 }
107 } else {// 若需要解析的数值位数(包括小数点与指数表达式大于18)
108 char coeff[] = new char[len];// 定义缓存字符数组coeff,长度等于需要解析的字符数量
109 for (; len > 0; offset++, len--) {
110 c = in[offset];// 获取当前字符
111 // have digit
112 if ((c >= '0' && c <= '9') || Character.isDigit(c)) {// 当前字符为数字
113 // First compact case, we need not to preserve the character
114 // and we can just compute the value in place.
115 if (c == '0' || Character.digit(c, 10) == 0) {
116 if (prec == 0) {// 当前字符为0且当前有效位数为0(表明是第一次解析出'0'字符),则保存至缓存字符数组
117 coeff[idx] = c;
118 prec = 1;// 将有效位数置为1,(即若第一字符为'0'也将计入有效数位),但是idx索引并不更新
119 } else if (idx != 0) {// 此时表明解析出的0是"中间"的'0'字符,此时将保存至缓存字符数组
120 coeff[idx++] = c;// 更新缓存数组的索引
121 ++prec;// 更新有效位数
122 } // 其实还存在第三种情况,即继首位为0之后连续存在多个0,此时什么也不做(不保存也不更新有效位数与缓存索引)
123 } else {// 当前字符是不为'0'的数字
124 if (prec != 1 || idx != 0)// 不执行此步骤唯一的情况是:首位或其后续连续几位为'0',则此时prec与idx都是0(这与上面的疑问处的处理是一样的,此时不增加有效位数)
125 ++prec; // prec unchanged if preceded by 0s
126 coeff[idx++] = c;// 只要出现非'0'数字都要保存至缓存字符数组
127 }
128 if (dot)// 若之前循环已经出现过'.'小数点,则有效小数位数自增,并且结束本次循环
129 ++scl;
130 continue;
131 }
132 // have dot
133 if (c == '.') {// 小数点,与上边的处理相同
134 // have dot
135 if (dot) // two dots
136 throw new NumberFormatException();
137 dot = true;
138 continue;
139 }
140 // exponent expected
141 if ((c != 'e') && (c != 'E'))// 若当前字符既不是数字也不是小数点也不是指数标志,则抛出数字格式化异常
142 throw new NumberFormatException();
143 exp = parseExp(in, offset, len);// 否则解析指数表达式的值,方法与上边相同
144 // Next test is required for backwards compatibility
145 if ((int) exp != exp) // overflow,指数溢出抛出异常
146 throw new NumberFormatException();
147 break; // [saves a test]
148 }
149 // here when no characters left
150 if (prec == 0) // no digits found,为解析出数值则抛出异常
151 throw new NumberFormatException();
152 // Adjust scale if exp is not zero.
153 if (exp != 0) { // had significant exponent,指数不为0则需要调整有效小数位数
154 scl = adjustScale(scl, exp);// 特别的有效小数位数可以为0(可以尝试一下,存在指数表达式时)
155 }
156 // Remove leading zeros from precision (digits count)
157 rb = new BigInteger(coeff, isneg ? -1 : 1, prec);// 根据解析出来的字符缓存数组构建BigInteger对象,这个里边大有文章,下片源码解析在进行分析
158 rs = compactValFor(rb);// 根据创建的BigInteger对象获取该对象所表示的数值(若可以表示使用long类型实际表示,否则使用long类型的最小值表示,例如超出表数范围)
159 int mcp = mc.precision;// 获取MathContext上下文中的有效小数位数(这个才是最终的有效小数位数,可以对原生的BigDecimal对象中的值的有效位数进行修改)
160 if (mcp > 0 && (prec > mcp)) {// 这个与上面的代码分析差不多,即MathContext中的有效位数要小于原生生成的BigDecimal对象中值的有效位数,需要进行舍入操作
161 if (rs == INFLATED) {// 若rs表示的数值是long类型的最小值(即该BigInteger的数值已经超出long类型的表数范围)
162 int drop = prec - mcp;// 获取需要舍去的有效位数
163 while (drop > 0) {// 循环舍去直到drop小于等于0
164 scl = checkScaleNonZero((long) scl - drop);// 若原有效小数位数减去舍去的位数,超过int的表数范围则抛出异常
165 rb = divideAndRoundByTenPow(rb, drop, mc.roundingMode.oldMode);// 进行舍去,见?
166 rs = compactValFor(rb);// 获取舍去后的BigInteger对象的值
167 if (rs != INFLATED) {// 获取舍去后的有效长度并结束循环,为啥结束循环看下面会继续处理
168 prec = longDigitLength(rs);
169 break;
170 }
171 prec = bigDigitLength(rb);// 否则获取有效长度后(对比上下两个获取有效长度的方法,是不一样的,rs是long类型的而rb是BigInteger类型的),获取最新的实际有效位数与MathContext之间的差值
172 drop = prec - mcp;
173 }
174 }
175 if (rs != INFLATED) {// 若rs已经是long类型的表数范围则进行一下处理,这个与处理字符长度小于等于18的那个分支是一样的,并且将之前创建的BigInteger对象赋值为null
176 int drop = prec - mcp;
177 while (drop > 0) {
178 scl = checkScaleNonZero((long) scl - drop);
179 rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
180 prec = longDigitLength(rs);
181 drop = prec - mcp;
182 }
183 rb = null;// 在该数值不能使用long类型表示时,该值才不为null.也就是说在能使用long类型表示时,使用rs表示该数值此时rb为null,而不能使用long类型表示时,使用rb表时该值,此时rs也存在但是该值并无用
184 }
185 }
186 }
187 } catch (ArrayIndexOutOfBoundsException e) {
188 throw new NumberFormatException();
189 } catch (NegativeArraySizeException e) {
190 throw new NumberFormatException();
191 }
192 this.scale = scl;
193 this.precision = prec;
194 this.intCompact = rs;
195 this.intVal = rb;
196 }
获取long类型整数的长度算法,看了好久
1 static int longDigitLength(long x) {
2 /*
3 * As described in "Bit Twiddling Hacks" by Sean Anderson,
4 * (http://graphics.stanford.edu/~seander/bithacks.html)
5 * integer log 10 of x is within 1 of (1233/4096)* (1 +
6 * integer log 2 of x). The fraction 1233/4096 approximates
7 * log10(2). So we first do a version of log2 (a variant of
8 * Long class with pre-checks and opposite directionality) and
9 * then scale and check against powers table. This is a little
10 * simpler in present context than the version in Hacker's
11 * Delight sec 11-4. Adding one to bit length allows comparing
12 * downward from the LONG_TEN_POWERS_TABLE that we need
13 * anyway.
14 */
15 assert x != BigDecimal.INFLATED;// 断言x不为long类型的最小值
16 if (x < 0)
17 x = -x;// 因为x不为long类型的最小值,因此可以这样赋值而不超过long类型的表数范围(long类型的表数范围-2^63~2^63-1,若x为最小值-2^63,取相反数-x后再赋值给x将会溢出)
18 if (x < 10) // must screen for 0, might as well 10,针对0这种特殊情况需要特殊判断
19 return 1;
20 int r = ((64 - Long.numberOfLeadingZeros(x) + 1) * 1233) >>> 12;// Long.numberOfLeadingZeros(x)方法获取x的二进制下最高非0位之前0所占的位数,这条语句使用的公式就是lg(X) ~= lg2*(1+log2(X)),而lg2~=1233/4096,4096就是2^12次方
这个公式我们就不研究了,有兴趣可以去上边官方注释中的那个网址中研究(英文的,祝各位好运),公式分析:lg(X)其实就是数X的十进制下的位数,log2(X)就是数X在二进制下的位数,那这个公式就可以理解为:long类型的整数X在十进制下的位数约等于1233/(2^12)乘以
(1+X在二进制下的位数).
21 long[] tab = LONG_TEN_POWERS_TABLE;// 整数数组,长度为19
22 // long类型的表数的最大值等于19位数值,等于数组LONG_TEN_POWERS_TABLE的长度,若r大于等于19,说明x的数值就是19位
23 return (r >= tab.length || x < tab[r]) ? r : r + 1;
24 }
获取long类型数值在二进制下最高非0位左侧的0所占的位数
1 public static int numberOfLeadingZeros(long i) {
2 // HD, Figure 5-6
3 if (i == 0)// 若i为0,则二进制下64位下均为0,所以返回64
4 return 64;
5 int n = 1;
6 int x = (int)(i >>> 32);// long类型的i右移32位并赋值给x,此时i原高位32位转化为地位32位
7 if (x == 0) { n += 32; x = (int)i; }// 若此时x等于0则表明i原高位32位为0,则n自增32并将i强转为int
// (这么做是因为已经判断出i的高位32位全部为0,接下来需要判断i的低位32位是否还有0的存在,将i强转int后就会将高位32位直接截掉剩余低位32位)
// 若该语句未执行则表明i高位32位存在非0数,此时x即为i的高位32位以进行接下来的判断
8 if (x >>> 16 == 0) { n += 16; x <<= 16; }// 接来下的操作与第一步区别不太大
9 if (x >>> 24 == 0) { n += 8; x <<= 8; }
10 if (x >>> 28 == 0) { n += 4; x <<= 4; }
11 if (x >>> 30 == 0) { n += 2; x <<= 2; }
12 n -= x >>> 31;// x为int类型,右移31位将只剩下最高位要么为0要么为1
// 若为0,因n之前直接赋值为1(即执行到上一步时其实n的值应必实际值多1),此时原n多出的1正好作为最高位0的计数:n -= 0
// 若为1,则表明x的最高位并不为0,英因此需要将原n多出的1减去:n -= 1
13 return n;
14 }
获取指定BigInteger对象中的值
1 private static long compactValFor(BigInteger b) {
2 int[] m = b.mag;// 这个mag就是BigInteger中实际存储数值的int类型的数组,而且需要注意的是其中元素也就是每个int的数值位数跟b的进位制有关,
3 特别的10进制的情况下,int的数位最多为9(因为int的表数范围是-2^31~2^31-1,也就是正负21亿,大概是10位,但是实际情况是99亿也是10位的
4 但已经超出int的表数范围,因此只能使用9位.而且从这一点我们也可以了解到BigInteger的mag属性中就是使用分段式的int数值来表示大整数,
5 比如:12345678987654321在十进制下,在mag中是以{12345678,987654321}这种形式来保存的)
6 int len = m.length;// 获取mag数组长度
7 if (len == 0)// 若长度为0则b就是0,直接返回0
8 return 0;
9 int d = m[0];// 否则获取数组第一位int数值
10 if (len > 2 || (len == 2 && d < 0))// mag数组的长度大于2或者等于2但首位int为负数,返回INFLATED(长度大于2那数值为数至少是1+2*9=19位
11 超出long类型的表数范围.其次因BigInteger的符号位是使用signnum属性表示,其mag数组中应不存在负值)
12 return INFLATED;
13
14 long u = (len == 2)?// 若mag的长度为1或2则可以使用long类型表示BigInteger对象的存储的数值
15 (((long) m[1] & LONG_MASK) + (((long)d) << 32)) :
16 (((long)d) & LONG_MASK);// LONG_MASK为long类型,二进制表示为所有位全部为1,这样任意一个long类型的整数"X & LONG_MASK"表达式的
17 结果是就是X
18 return (b.signum < 0)? -u : u;// 将运算结果加上数值原符号
19 }
以十进制进行舍入(做除法进行舍入多余的有效数字)
1 private static BigInteger divideAndRoundByTenPow(BigInteger intVal, int tenPow, int roundingMode) {// intVal:需要进行进行舍入操作的原始数值
tenPow:10的次幂(2就代表10^2次方) roundingMode:舍入模式
2 if (tenPow < LONG_TEN_POWERS_TABLE.length)// LONG_TEN_POWERS_TABLE:指数与10的次幂的映射,指数映射数组索引而10的次幂映射数组的元素(例如10^3就代表该数组的索引为3的元素为10^3次方)
该数组长度为19,即最高位10^18次方,至于数组长度为什么是19这是因为long类型的表数范围决定的
根据tenPow<19与否决定执行那个方法
3 intVal = divideAndRound(intVal, LONG_TEN_POWERS_TABLE[tenPow], roundingMode);
4 else
5 intVal = divideAndRound(intVal, bigTenToThe(tenPow), roundingMode);
6 return intVal;
7 }
见下面?
1 // 情形二首先计算应该除以的10的次幂的指数
2 private static BigInteger bigTenToThe(int n) {
3 if (n < 0)// 指数小于0,这里的指数代表的是应该舍去的位数,一般的小于0并不会进入这个方法
4 return BigInteger.ZERO;
5
6 if (n < BIG_TEN_POWERS_TABLE_MAX) {// BIG_TEN_POWERS_TABLE_MAX为常量,等于19*16
7 BigInteger[] pows = BIG_TEN_POWERS_TABLE;// 将pows指向常量数组BIG_TEN_POWERS_TABLE,就是上面所说的那个指数与10的次幂映射的数组
8 if (n < pows.length)// 若n小于原生的数组的长度,说明10^n次幂包含在数组中直接返回就行
9 return pows[n];
10 else // 否则将会拓展这个数组,原数组只能表示到10^18次方,这时候看上面的16*19这个表达式,这就看明白这个数组拓展是有极限的,极限就是最多拓展到16倍
11 // 下面这个方法就是拓展这个数组的,这样做可能为了多次创建的时候可以这季节返回常量而不需要创建额外的BigInteger对象,例如最后一步
12
13 return expandBigIntegerTenPowers(n);
14 }
15
16 return BigInteger.TEN.pow(n);// 否则创建10^n次幂
17 }
1 // 情形一:
2 private static BigInteger divideAndRound(BigInteger bdividend, long ldivisor, int roundingMode) {// 解释一下参数的单词,代表啥自己猜
3 // bdividend:被除数 ldivisor:除数 quotient:商
4 boolean isRemainderZero; // record remainder is zero or not,记录余数是否为0的标志位
5 int qsign; // quotient sign,商的符号
6 long r = 0; // store quotient & remainder in long
7 MutableBigInteger mq = null; // store quotient,使用MutableBigInteger保存商
8 // Descend into mutables for faster remainder checks
9 MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);// 根据被除数创建MutableBigInteger对象,这个才是真正的被除数
10 mq = new MutableBigInteger();// 初始化商,构建为MutableBigInteger对象
11 r = mdividend.divide(ldivisor, mq);// 使用被除数调用方法除以除数ldivisor,返回商mq与余数r
12 isRemainderZero = (r == 0);// 判断余数是否为0
13 qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;// 商的符号将根据除数与被除数共同决定
14 if (!isRemainderZero) {// 若不能整除时,
15 if(needIncrement(ldivisor, roundingMode, qsign, mq, r)) {// needIncrement()方法根据RoundingMode是否需要增加商,若需要则商将增加ONE
16 mq.add(MutableBigInteger.ONE);
17 }
18 }
19 return mq.toBigInteger(qsign);// 根据商的符号qsign与MutableBigInteger类型的商mq创建BigInteger对象并返回
20 }