最早由于做作業,結識了java的BigInrger類。讀着讀着,越來越覺得有趣。後來作業做完,也不忍丢下它,索性把全部代碼研究一遍。
開始的時候,一個上午時間最多讀懂2個方法。但是還是有滋有味的堅持了下來。下面開始一點點剖開它“隐藏”的秘密。
首先要想搞懂兩個問題:BigIngeter類的目的——實作高精度數的存儲和計算。基礎的實作機理——用int型(32位)數組存儲資料。(在代碼的注釋中有詳細說明)
/
BigInteger類中的屬性:{
int signum; 符号位,負數是為-1,零時為0,正數是為1
int[] mag; The magnitude of this BigInteger,大數的值 //其他輔助變量暫時先不看
}
首先來分析下構造函數 (構造五部曲:1.檢查是否符合标準 2.去零 3.mag指派 4.去mag中零 5.符号位指派)
1. 使用byte(8位)型數組構造BigInteger:
/
public BigInteger(byte[] val) {
if (val.length == 0)
throw new NumberFormatException("Zero length BigInteger"); //傳入數組長度為零,報錯
if (val[0] < 0) {
mag = makePositive(val);
signum = -1; //如果數組第一個值為負數,則将數組變正存入mag,signum賦-1
} else {
mag = stripLeadingZeroBytes(val); //如果非負,則可直接去掉前面無效零,再賦給mag
signum = (mag.length == 0 ? 0 : 1);
}
}
下面看一下具體調用的函數
///
private static int[] stripLeadingZeroBytes(byte a[]) {
int byteLength = a.length;
int keep;
// Find first nonzero byte
for (keep=0; keep<a.length && a[keep]==0; keep++) //找到第一個有效位,并用keep記錄下
;
// Allocate new array and copy relevant part of input array
int intLength = ((byteLength - keep) + 3)/4; //計算int[]的長度,byte[1/2/3/4]對應int[1]
int[] result = new int[intLength];
int b = byteLength - 1;
for (int i = intLength-1; i >= 0; i--) {
result[i] = a[b--] & 0xff; //向int[]指派,&0xff的作用是消除對int前24位的影響
(計算機中使用補碼存儲資料,如果直接将一個第一位為“1”的byte值賦給int,則前24為将為“1”)
int bytesRemaining = b - keep + 1;
int bytesToTransfer = Math.min(3, bytesRemaining);
for (int j=8; j <= 8*bytesToTransfer; j += 8)
result[i] |= ((a[b--] & 0xff) << j); //進行移位,每次移動8位,再進行或運算
}
return result;
}
//
private static int[] makePositive(byte a[]) { int keep, k; int byteLength = a.length;
// Find first non-sign (0xff) byte of input for (keep=0; keep<byteLength && a[keep]==-1; keep++) //找出非符号位(此處我看了很久才看懂)。若a[]=-1,即計算機中二進制為“11111111”,在int型中全為“1”的前幾位被認為是符号位。要想轉換成正的int值,隻需要後幾位即可。 ;
for (k=keep; k<byteLength && a[k]==0; k++) //由于傳入參數數組第一個值必為負(由構造函數可得),是以不必考慮去零,變量k的作用隻是判斷需要“額外”位 ;
int extraByte = (k==byteLength) ? 1 : 0; //如果除符号位以外的全部為“0”,則需要“額外”1位來存儲資料 int intLength = ((byteLength - keep + extraByte) + 3)/4; int result[] = new int[intLength];
int b = byteLength - 1; for (int i = intLength-1; i >= 0; i--) { result[i] = a[b--] & 0xff; int numBytesToTransfer = Math.min(3, b-keep+1); if (numBytesToTransfer < 0) numBytesToTransfer = 0; for (int j=8; j <= 8*numBytesToTransfer; j += 8) result[i] |= ((a[b--] & 0xff) << j);
// Mask indicates which bits must be complemented int mask = -1 >>> (8*(3-numBytesToTransfer)); //将負值變為正值,即由原碼轉反碼 result[i] = ~result[i] & mask; }
// Add one to one's complement to generate two's complement for (int i=result.length-1; i>=0; i--) { result[i] = (int)((result[i] & LONG_MASK) + 1); //long LONG_MASK = 0xffffffffL;為了進行位運算而不考慮int符号問題 if (result[i] != 0) //(這個地方也把我蒙騙了好久)突然恍悟,其實就是+1後不為零,即不需要進位,就break退出吧! break; }
return result; }
2. 使用int(32位)型數組構造BigInteger: / private BigInteger(int[] val) { if (val.length == 0) throw new NumberFormatException("Zero length BigInteger");
if (val[0] < 0) { mag = makePositive(val); signum = -1; } else { mag = trustedStripLeadingZeroInts(val); signum = (mag.length == 0 ? 0 : 1); } }
與byte[]構造原理相同,并且更為簡單,不重述。有一點說明,這裡使用trustedStripLeadingZeroInts可信賴的去零方法,與StripLeadingZeroInts的差別在于,對于可信賴的去零方法,如果沒有無效零,則直接傳回原數組,不進行複制。