最近的項目中用到了BigDecimal,之前並沒有深入學習使用過,只是大概知道可以用於精確的運算,而float和double是不精確的。
BigDecimal的實現中用到了BigIntegr,因此這裏先學習下BigInteger。
BigInteger
int和long都有長度限制,如果需要計算的整數大小超過long的範圍,那麼可以用到BigInteger。
BigInteger繼承自抽象類Number。
// 符號,-1-負數,0-0,1-正數
final int signum;
//以大尾數順序表示的這個大整數的大小:這個數組的第0個元素是該大小中最重要的整數。
//大小必須“最小”,因爲最重要的int(@code mag[0])必須爲非零。
//這對於確保每個biginteger值只有一個表示是必要的。注意,這意味着biginteger zero有一個零長度的mag數組。
// mag表示的是正數的原碼字節數組。mag數組是存儲BigInteger數值大小的,採用big-endian的順序,也就是高位字節存入低地址,低位字節存入高地址.
// http://www.mamicode.com/info-detail-1360414.html
final int[] mag;
// 冗餘字段
private int bitCount;
// 冗餘字段
private int bitLength;
// 冗餘字段
private int lowestSetBit;
// 冗餘字段
private int firstNonzeroIntNum;
// 用於獲取int值
final static long LONG_MASK = 0xffffffffL;
// 數組最大長度
private static final int MAX_MAG_LENGTH = Integer.MAX_VALUE / Integer.SIZE + 1; // (1 << 26)
// PRIME_SEARCH_BIT_LENGTH_LIMIT 大於這個長度的大數會導致BitSieve.singleSearch方法溢出
private static final int PRIME_SEARCH_BIT_LENGTH_LIMIT = 500000000;
// 兩個大數的mag[] 長度都大於這個值,將會使用Karatsuba multiplication
private static final int KARATSUBA_THRESHOLD = 80;
// 如果兩個數mag長度都大於KARATSUBA_THRESHOLD,且至少一個的長度大於這個限度,將會使用3-way Toom-Cook multiplication
private static final int TOOM_COOK_THRESHOLD = 240;
// 如果大數的數組長度大於該限制,將會使用Karatsuba squaring
private static final int KARATSUBA_SQUARE_THRESHOLD = 128;
// 如果大數的數組長度大於該限制,將會使用Toom-Cook squaring
private static final int TOOM_COOK_SQUARE_THRESHOLD = 216;
// 下面幾個參數作用類似,暫時不記錄,等到以後需要時再細看
static final int BURNIKEL_ZIEGLER_THRESHOLD = 80;
static final int BURNIKEL_ZIEGLER_OFFSET = 40;
private static final int SCHOENHAGE_BASE_CONVERSION_THRESHOLD = 20;
private static final int MULTIPLY_SQUARE_THRESHOLD = 20;
private static final int MONTGOMERY_INTRINSIC_THRESHOLD = 512;
構造函數
// 該部分註釋來自於 http://www.mamicode.com/info-detail-1360414.html
public BigInteger(byte[] val) {
if (val.length == 0)
throw new NumberFormatException("Zero length BigInteger");
// 如果第一個字節是負數,則這個byte[] val就是負數的補碼。因此通過補碼的逆運算(補碼的補碼)可以得到負數的絕對值,再將符號位設置爲-,則得到這個補碼所代表的負數。
// 如果參數字節數組以-1開頭,不管幾個,只要-1是連續的,那麼這些-1都看成是符號-,這些-1的下一個字節纔是有效字節。如果不以-1開頭而是其他負數,則有效字節從索引0開始。
if (val[0] < 0) {
mag = makePositive(val);
signum = -1;
} else {
// 如果第一個字節是整數,則採用stripLeadingZeroBytes方法,將每個字節的二進制補碼按順序連接起來後去掉開頭的0後返回。
mag = stripLeadingZeroBytes(val);
signum = (mag.length == 0 ? 0 : 1);
}
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
// 將一個包含大數的二進制補碼的的int數組轉換爲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);
}
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
// 在上邊兩個構造函數中加上了signum來判斷符號的正負
public BigInteger(int signum, byte[] magnitude);
private BigInteger(int signum, int[] magnitude)
// 把val按照radix進制轉化爲大數,val中可以包含一個可選的-或+,不可以有空格
public BigInteger(String val, int radix)
// 10進制的BigInteger(String val, int radix)方法
public BigInteger(String val)
其他的暫時看不下去了,等待需要用到的時候看吧,令人頭禿。
先看幾個常用的方法:
// 返回一個大整數,其值等於指定的@code long。此“靜態工廠方法”優先於(@code long)構造函數提供,因爲它允許重用常用的大整數。
// 如果值在-16-16之間,那麼返回的BigInteger是同一個對象。
public static BigInteger valueOf(long val)
// 原因,以下代碼在靜態代碼塊中,返回時就是返回這兩個數組對應位置的對象
for (int i = 1; i <= MAX_CONSTANT; i++) {
int[] magnitude = new int[1];
magnitude[0] = i;
posConst[i] = new BigInteger(magnitude, 1);
negConst[i] = new BigInteger(magnitude, -1);
}
使用BigInteger做加減乘除運算時,分別需要調用實例方法:
BigInteger test1 = BigInteger.valueOf(7);
BigInteger test2 = BigInteger.valueOf(8);
BigInteger testNeg = BigInteger.valueOf(-1);
BigInteger r1 = test1.add(test2);
BigInteger r2 = test1.subtract(test2);
BigInteger r3 = test1.multiply(test2);
BigInteger r4 = test1.divide(test2);
和long相比,BigInteger不會有長度限制,但是計算效率較低。
BigInteger也是不可變類,可以轉換爲基本類型,轉換時會丟失高位信息。
BigDecimal
BigDecimal可以表示一個任意大小且精度完全準確的浮點數。
// 待完善
private final BigInteger intVal;
// 表示小數位數
private final int scale;
// 小數的位數,如果小數位數未知則爲0,如果非0,保證值時正確的
private transient int precision;
// 用於存儲規範的字符串表示形式
private transient String stringCache;
// intCompact的值,表示有意義的部分只能從intVal中獲得
static final long INFLATED = Long.MIN_VALUE;
private static final BigInteger INFLATED_BIGINT = BigInteger.valueOf(INFLATED);
// 如果此bigdecimal的有效位的絕對值小於或等於@code long.max,則該值可以緊湊地存儲在此字段中並用於計算。
private final transient long intCompact;
// 所有18位十進制字符串都適合一個long;並非所有19位字符串都適合
private static final int MAX_COMPACT_DIGITS = 18;