java的BigInteger和BigDecimal类源码详解
如果基本的整数和浮点数精度不能满足需求,可以使用BigInteger和BigDecimal处理任意长度数字序列的数值,但缺点是速度比较慢。
BigInteger实现了任意精度的整数运算,BigDecimal实现了任意精度的浮点数运算。
BigInteger
类的定义
public class BigInteger extends Number implements Comparable {
final int signum;
final int[] mag;
// These "redundant fields" are initialized with recognizable nonsense
// values, and cached the first time they are needed (or never, if they
// aren't needed).
@Deprecated
private int bitCount;
@Deprecated
private int bitLength;
@Deprecated
private int lowestSetBit;
@Deprecated
private int firstNonzeroIntNum;
final static long LONG_MASK = 0xffffffffL;
private static final int MAX_MAG_LENGTH = Integer.MAX_VALUE / Integer.SIZE + 1; // (1 = MAX_MAG_LENGTH) {
checkRange();
}
}
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();
}
}
public BigInteger(int signum, byte[] magnitude) {
this.mag = stripLeadingZeroBytes(magnitude);
if (signum < -1 || signum > 1)
throw(new NumberFormatException("Invalid signum value"));
if (this.mag.length == 0) {
this.signum = 0;
} else {
if (signum == 0)
throw(new NumberFormatException("signum-magnitude mismatch"));
this.signum = signum;
}
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
private BigInteger(int signum, int[] magnitude) {
this.mag = stripLeadingZeroInts(magnitude);
if (signum < -1 || signum > 1)
throw(new NumberFormatException("Invalid signum value"));
if (this.mag.length == 0) {
this.signum = 0;
} else {
if (signum == 0)
throw(new NumberFormatException("signum-magnitude mismatch"));
this.signum = signum;
}
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
public BigInteger(String val, int radix) {
int cursor = 0, numDigits;
final int len = val.length();
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
throw new NumberFormatException("Radix out of range");
if (len == 0)
throw new NumberFormatException("Zero length BigInteger");
// Check for at most one leading sign
int sign = 1;
int index1 = val.lastIndexOf('-');
int index2 = val.lastIndexOf('+');
if (index1 >= 0) {
if (index1 != 0 || index2 >= 0) {
throw new NumberFormatException("Illegal embedded sign character");
}
sign = -1;
cursor = 1;
} else if (index2 >= 0) {
if (index2 != 0) {
throw new NumberFormatException("Illegal embedded sign character");
}
cursor = 1;
}
if (cursor == len)
throw new NumberFormatException("Zero length BigInteger");
// Skip leading zeros and compute number of digits in magnitude
while (cursor < len &&
Character.digit(val.charAt(cursor), radix) == 0) {
cursor++;
}
if (cursor == len) {
signum = 0;
mag = ZERO.mag;
return;
}
numDigits = len - cursor;
signum = sign;
// Pre-allocate array of expected size. May be too large but can
// never be too small. Typically exact.
long numBits = ((numDigits * bitsPerDigit[radix]) >>> 10) + 1;
if (numBits + 31 >= (1L >> 5;
int[] magnitude = new int[numWords];
// Process first (potentially short) digit group
int firstGroupLen = numDigits % digitsPerInt[radix];
if (firstGroupLen == 0)
firstGroupLen = digitsPerInt[radix];
String group = val.substring(cursor, cursor += firstGroupLen);
magnitude[numWords - 1] = Integer.parseInt(group, radix);
if (magnitude[numWords - 1] < 0)
throw new NumberFormatException("Illegal digit");
// Process remaining digit groups
int superRadix = intRadix[radix];
int groupVal = 0;
while (cursor < len) {
group = val.substring(cursor, cursor += digitsPerInt[radix]);
groupVal = Integer.parseInt(group, radix);
if (groupVal < 0)
throw new NumberFormatException("Illegal digit");
destructiveMulAdd(magnitude, superRadix, groupVal);
}
// Required for cases where the array was overallocated.
mag = trustedStripLeadingZeroInts(magnitude);
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
BigInteger(char[] val, int sign, int len) {
int cursor = 0, numDigits;
// Skip leading zeros and compute number of digits in magnitude
while (cursor < len && Character.digit(val[cursor], 10) == 0) {
cursor++;
}
if (cursor == len) {
signum = 0;
mag = ZERO.mag;
return;
}
numDigits = len - cursor;
signum = sign;
// Pre-allocate array of expected size
int numWords;
if (len < 10) {
numWords = 1;
} else {
long numBits = ((numDigits * bitsPerDigit[10]) >>> 10) + 1;
if (numBits + 31 >= (1L >> 5;
}
int[] magnitude = new int[numWords];
// Process first (potentially short) digit group
int firstGroupLen = numDigits % digitsPerInt[10];
if (firstGroupLen == 0)
firstGroupLen = digitsPerInt[10];
magnitude[numWords - 1] = parseInt(val, cursor, cursor += firstGroupLen);
// Process remaining digit groups
while (cursor < len) {
int groupVal = parseInt(val, cursor, cursor += digitsPerInt[10]);
destructiveMulAdd(magnitude, intRadix[10], groupVal);
}
mag = trustedStripLeadingZeroInts(magnitude);
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
// Create an integer with the digits between the two indexes
// Assumes start < end. The result may be negative, but it
// is to be treated as an unsigned value.
private int parseInt(char[] source, int start, int end) {
int result = Character.digit(source[start++], 10);
if (result == -1)
throw new NumberFormatException(new String(source));
for (int index = start; index < end; index++) {
int nextVal = Character.digit(source[index], 10);
if (nextVal == -1)
throw new NumberFormatException(new String(source));
result = 10*result + nextVal;
}
return result;
}
// bitsPerDigit in the given radix times 1024
// Rounded up to avoid underallocation.
private static long bitsPerDigit[] = { 0, 0,
1024, 1624, 2048, 2378, 2648, 2875, 3072, 3247, 3402, 3543, 3672,
3790, 3899, 4001, 4096, 4186, 4271, 4350, 4426, 4498, 4567, 4633,
4696, 4756, 4814, 4870, 4923, 4975, 5025, 5074, 5120, 5166, 5210,
5253, 5295};
// Multiply x array times word y in place, and add word z
private static void destructiveMulAdd(int[] x, int y, int z) {
// Perform the multiplication word by word
long ylong = y & LONG_MASK;
long zlong = z & LONG_MASK;
int len = x.length;
long product = 0;
long carry = 0;
for (int i = len-1; i >= 0; i--) {
product = ylong * (x[i] & LONG_MASK) + carry;
x[i] = (int)product;
carry = product >>> 32;
}
// Perform the addition
long sum = (x[len-1] & LONG_MASK) + zlong;
x[len-1] = (int)sum;
carry = sum >>> 32;
for (int i = len-2; i >= 0; i--) {
sum = (x[i] & LONG_MASK) + carry;
x[i] = (int)sum;
carry = sum >>> 32;
}
}
public BigInteger(String val) {
this(val, 10);
}
public BigInteger(int numBits, Random rnd) {
this(1, randomBits(numBits, rnd));
}
private static byte[] randomBits(int numBits, Random rnd) {
if (numBits < 0)
throw new IllegalArgumentException("numBits must be non-negative");
int numBytes = (int)(((long)numBits+7)/8); // avoid overflow
byte[] randomBits = new byte[numBytes];
// Generate random bytes and mask out any excess bits
if (numBytes > 0) {
rnd.nextBytes(randomBits);
int excessBits = 8*numBytes - numBits;
randomBits[0] &= (1 >> 5;
int temp[] = new int[magLen];
int highBit = 1 2
BigInteger p = new BigInteger(temp, 1);
// Do cheap "pre-test" if applicable
if (bitLength > 6) {
long r = p.remainder(SMALL_PRIME_PRODUCT).longValue();
if ((r%3==0) || (r%5==0) || (r%7==0) || (r%11==0) ||
(r%13==0) || (r%17==0) || (r%19==0) || (r%23==0) ||
(r%29==0) || (r%31==0) || (r%37==0) || (r%41==0))
continue; // Candidate is composite; try another
}
// All candidates of bitLength 2 and 3 are prime by this point
if (bitLength < 4)
return p;
// Do expensive test if we survive pre-test (or it's inapplicable)
if (p.primeToCertainty(certainty, rnd))
return p;
}
}
private static final BigInteger SMALL_PRIME_PRODUCT
= valueOf(3L*5*7*11*13*17*19*23*29*31*37*41);
private static BigInteger largePrime(int bitLength, int certainty, Random rnd) {
BigInteger p;
p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
p.mag[p.mag.length-1] &= 0xfffffffe;
// Use a sieve length likely to contain the next prime number
int searchLen = getPrimeSearchLen(bitLength);
BitSieve searchSieve = new BitSieve(p, searchLen);
BigInteger candidate = searchSieve.retrieve(p, certainty, rnd);
while ((candidate == null) || (candidate.bitLength() != bitLength)) {
p = p.add(BigInteger.valueOf(2*searchLen));
if (p.bitLength() != bitLength)
p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
p.mag[p.mag.length-1] &= 0xfffffffe;
searchSieve = new BitSieve(p, searchLen);
candidate = searchSieve.retrieve(p, certainty, rnd);
}
return candidate;
}
public BigInteger nextProbablePrime() {
if (this.signum < 0)
throw new ArithmeticException("start < 0: " + this);
// Handle trivial cases
if ((this.signum == 0) || this.equals(ONE))
return TWO;
BigInteger result = this.add(ONE);
// Fastpath for small numbers
if (result.bitLength() < SMALL_PRIME_THRESHOLD) {
// Ensure an odd number
if (!result.testBit(0))
result = result.add(ONE);
while (true) {
// Do cheap "pre-test" if applicable
if (result.bitLength() > 6) {
long r = result.remainder(SMALL_PRIME_PRODUCT).longValue();
if ((r%3==0) || (r%5==0) || (r%7==0) || (r%11==0) ||
(r%13==0) || (r%17==0) || (r%19==0) || (r%23==0) ||
(r%29==0) || (r%31==0) || (r%37==0) || (r%41==0)) {
result = result.add(TWO);
continue; // Candidate is composite; try another
}
}
// All candidates of bitLength 2 and 3 are prime by this point
if (result.bitLength() < 4)
return result;
// The expensive test
if (result.primeToCertainty(DEFAULT_PRIME_CERTAINTY, null))
return result;
result = result.add(TWO);
}
}
// Start at previous even number
if (result.testBit(0))
result = result.subtract(ONE);
// Looking for the next large prime
int searchLen = getPrimeSearchLen(result.bitLength());
while (true) {
BitSieve searchSieve = new BitSieve(result, searchLen);
BigInteger candidate = searchSieve.retrieve(result,
DEFAULT_PRIME_CERTAINTY, null);
if (candidate != null)
return candidate;
result = result.add(BigInteger.valueOf(2 * searchLen));
}
}
private static int getPrimeSearchLen(int bitLength) {
if (bitLength > PRIME_SEARCH_BIT_LENGTH_LIMIT + 1) {
throw new ArithmeticException("Prime search implementation restriction on bitLength");
}
return bitLength / 20 * 64;
}
boolean primeToCertainty(int certainty, Random random) {
int rounds = 0;
int n = (Math.min(certainty, Integer.MAX_VALUE-1)+1)/2;
// The relationship between the certainty and the number of rounds
// we perform is given in the draft standard ANSI X9.80, "PRIME
// NUMBER GENERATION, PRIMALITY TESTING, AND PRIMALITY CERTIFICATES".
int sizeInBits = this.bitLength();
if (sizeInBits < 100) {
rounds = 50;
rounds = n < rounds ? n : rounds;
return passesMillerRabin(rounds, random);
}
if (sizeInBits < 256) {
rounds = 27;
} else if (sizeInBits < 512) {
rounds = 15;
} else if (sizeInBits < 768) {
rounds = 8;
} else if (sizeInBits < 1024) {
rounds = 4;
} else {
rounds = 2;
}
rounds = n < rounds ? n : rounds;
return passesMillerRabin(rounds, random) && passesLucasLehmer();
}
private boolean passesLucasLehmer() {
BigInteger thisPlusOne = this.add(ONE);
// Step 1
int d = 5;
while (jacobiSymbol(d, this) != -1) {
// 5, -7, 9, -11, ...
d = (d < 0) ? Math.abs(d)+2 : -(d+2);
}
// Step 2
BigInteger u = lucasLehmerSequence(d, thisPlusOne, this);
// Step 3
return u.mod(this).equals(ZERO);
}
private static int jacobiSymbol(int p, BigInteger n) {
if (p == 0)
return 0;
// Algorithm and comments adapted from Colin Plumb's C library.
int j = 1;
int u = n.mag[n.mag.length-1];
// Make p positive
if (p < 0) {
p = -p;
int n8 = u & 7;
if ((n8 == 3) || (n8 == 7))
j = -j; // 3 (011) or 7 (111) mod 8
}
// Get rid of factors of 2 in p
while ((p & 3) == 0)
p >>= 2;
if ((p & 1) == 0) {
p >>= 1;
if (((u ^ (u>>1)) & 2) != 0)
j = -j; // 3 (011) or 5 (101) mod 8
}
if (p == 1)
return j;
// Then, apply quadratic reciprocity
if ((p & u & 2) != 0) // p = u = 3 (mod 4)?
j = -j;
// And reduce u mod p
u = n.mod(BigInteger.valueOf(p)).intValue();
// Now compute Jacobi(u,p), u < p
while (u != 0) {
while ((u & 3) == 0)
u >>= 2;
if ((u & 1) == 0) {
u >>= 1;
if (((p ^ (p>>1)) & 2) != 0)
j = -j; // 3 (011) or 5 (101) mod 8
}
if (u == 1)
return j;
// Now both u and p are odd, so use quadratic reciprocity
assert (u < p);
int t = u; u = p; p = t;
if ((u & p & 2) != 0) // u = p = 3 (mod 4)?
j = -j;
// Now u >= p, so it can be reduced
u %= p;
}
return 0;
}
private static BigInteger lucasLehmerSequence(int z, BigInteger k, BigInteger n) {
BigInteger d = BigInteger.valueOf(z);
BigInteger u = ONE; BigInteger u2;
BigInteger v = ONE; BigInteger v2;
for (int i=k.bitLength()-2; i >= 0; i--) {
u2 = u.multiply(v).mod(n);
v2 = v.square().add(d.multiply(u.square())).mod(n);
if (v2.testBit(0))
v2 = v2.subtract(n);
v2 = v2.shiftRight(1);
u = u2; v = v2;
if (k.testBit(i)) {
u2 = u.add(v).mod(n);
if (u2.testBit(0))
u2 = u2.subtract(n);
u2 = u2.shiftRight(1);
v2 = v.add(d.multiply(u)).mod(n);
if (v2.testBit(0))
v2 = v2.subtract(n);
v2 = v2.shiftRight(1);
u = u2; v = v2;
}
}
return u;
}
private boolean passesMillerRabin(int iterations, Random rnd) {
// Find a and m such that m is odd and this == 1 + 2**a * m
BigInteger thisMinusOne = this.subtract(ONE);
BigInteger m = thisMinusOne;
int a = m.getLowestSetBit();
m = m.shiftRight(a);
// Do the tests
if (rnd == null) {
rnd = ThreadLocalRandom.current();
}
for (int i=0; i < iterations; i++) {
// Generate a uniform random on (1, this)
BigInteger b;
do {
b = new BigInteger(this.bitLength(), rnd);
} while (b.compareTo(ONE) = 0);
int j = 0;
BigInteger z = b.modPow(m, this);
while (!((j == 0 && z.equals(ONE)) || z.equals(thisMinusOne))) {
if (j > 0 && z.equals(ONE) || ++j == a)
return false;
z = z.modPow(TWO, this);
}
}
return true;
}
BigInteger(int[] magnitude, int signum) {
this.signum = (magnitude.length == 0 ? 0 : signum);
this.mag = magnitude;
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
private BigInteger(byte[] magnitude, int signum) {
this.signum = (magnitude.length == 0 ? 0 : signum);
this.mag = stripLeadingZeroBytes(magnitude);
if (mag.length >= MAX_MAG_LENGTH) {
checkRange();
}
}
private void checkRange() {
if (mag.length > MAX_MAG_LENGTH || mag.length == MAX_MAG_LENGTH && mag[0] < 0) {
reportOverflow();
}
}
private static void reportOverflow() {
throw new ArithmeticException("BigInteger would overflow supported range");
}
//Static Factory Methods
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 negConst[(int) -val];
return new BigInteger(val);
}
private BigInteger(long val) {
if (val < 0) {
val = -val;
signum = -1;
} else {
signum = 1;
}
int highWord = (int)(val >>> 32);
if (highWord == 0) {
mag = new int[1];
mag[0] = (int)val;
} else {
mag = new int[2];
mag[0] = highWord;
mag[1] = (int)val;
}
}
private static BigInteger valueOf(int val[]) {
return (val[0] > 0 ? new BigInteger(val, 1) : new BigInteger(val));
}
使用静态的valueOf方法可以将普通的数值转为大数值。
BigInteger
和Integer
、Long
一样,也是不可变类,并且也继承自Number
类。因为Number
定义了转换为基本类型的几个方法:
- 转换为
byte
:byteValue()
- 转换为
short
:shortValue()
- 转换为
int
:intValue()
- 转换为
long
:longValue()
- 转换为
float
:floatValue()
- 转换为
double
:doubleValue()
因此,通过上述方法,可以把BigInteger
转换成基本类型。如果BigInteger
表示的范围超过了基本类型的范围,转换时将丢失高位信息,即结果不一定是准确的。如果需要准确地转换成基本类型,可以使用intValueExact()
、longValueExact()
等方法,在转换时如果超出范围,将直接抛出ArithmeticException
异常。
public long longValueExact() {
if (mag.length 32);
return result;
} else {
result = new int[xIndex];
sum = (x[--xIndex] & LONG_MASK) + (val & LONG_MASK);
result[xIndex] = (int)sum;
sum = (x[--xIndex] & LONG_MASK) + (highWord & LONG_MASK) + (sum >>> 32);
result[xIndex] = (int)sum;
}
}
// Copy remainder of longer number while carry propagation is required
boolean carry = (sum >>> 32 != 0);
while (xIndex > 0 && carry)
carry = ((result[--xIndex] = x[xIndex] + 1) == 0);
// Copy remainder of longer number
while (xIndex > 0)
result[--xIndex] = x[xIndex];
// Grow result if necessary
if (carry) {
int bigger[] = new int[result.length + 1];
System.arraycopy(result, 0, bigger, 1, result.length);
bigger[0] = 0x01;
return bigger;
}
return result;
}
private static int[] add(int[] x, int[] y) {
// If x is shorter, swap the two arrays
if (x.length < y.length) {
int[] tmp = x;
x = y;
y = tmp;
}
int xIndex = x.length;
int yIndex = y.length;
int result[] = new int[xIndex];
long sum = 0;
if (yIndex == 1) {
sum = (x[--xIndex] & LONG_MASK) + (y[0] & LONG_MASK) ;
result[xIndex] = (int)sum;
} else {
// Add common parts of both numbers
while (yIndex > 0) {
sum = (x[--xIndex] & LONG_MASK) +
(y[--yIndex] & LONG_MASK) + (sum >>> 32);
result[xIndex] = (int)sum;
}
}
// Copy remainder of longer number while carry propagation is required
boolean carry = (sum >>> 32 != 0);
while (xIndex > 0 && carry)
carry = ((result[--xIndex] = x[xIndex] + 1) == 0);
// Copy remainder of longer number
while (xIndex > 0)
result[--xIndex] = x[xIndex];
// Grow result if necessary
if (carry) {
int bigger[] = new int[result.length + 1];
System.arraycopy(result, 0, bigger, 1, result.length);
bigger[0] = 0x01;
return bigger;
}
return result;
}
private static int[] subtract(long val, int[] little) {
int highWord = (int)(val >>> 32);
if (highWord == 0) {
int result[] = new int[1];
result[0] = (int)(val - (little[0] & LONG_MASK));
return result;
} else {
int result[] = new int[2];
if (little.length == 1) {
long difference = ((int)val & LONG_MASK) - (little[0] & LONG_MASK);
result[1] = (int)difference;
// Subtract remainder of longer number while borrow propagates
boolean borrow = (difference >> 32 != 0);
if (borrow) {
result[0] = highWord - 1;
} else { // Copy remainder of longer number
result[0] = highWord;
}
return result;
} else { // little.length == 2
long difference = ((int)val & LONG_MASK) - (little[1] & LONG_MASK);
result[1] = (int)difference;
difference = (highWord & LONG_MASK) - (little[0] & LONG_MASK) + (difference >> 32);
result[0] = (int)difference;
return result;
}
}
}
private static int[] subtract(int[] big, long val) {
int highWord = (int)(val >>> 32);
int bigIndex = big.length;
int result[] = new int[bigIndex];
long difference = 0;
if (highWord == 0) {
difference = (big[--bigIndex] & LONG_MASK) - val;
result[bigIndex] = (int)difference;
} else {
difference = (big[--bigIndex] & LONG_MASK) - (val & LONG_MASK);
result[bigIndex] = (int)difference;
difference = (big[--bigIndex] & LONG_MASK) - (highWord & LONG_MASK) + (difference >> 32);
result[bigIndex] = (int)difference;
}
// Subtract remainder of longer number while borrow propagates
boolean borrow = (difference >> 32 != 0);
while (bigIndex > 0 && borrow)
borrow = ((result[--bigIndex] = big[bigIndex] - 1) == -1);
// Copy remainder of longer number
while (bigIndex > 0)
result[--bigIndex] = big[bigIndex];
return result;
}
public BigInteger subtract(BigInteger val) {
if (val.signum == 0)
return this;
if (signum == 0)
return val.negate();
if (val.signum != signum)
return new BigInteger(add(mag, val.mag), signum);
int cmp = compareMagnitude(val);
if (cmp == 0)
return ZERO;
int[] resultMag = (cmp > 0 ? subtract(mag, val.mag)
: subtract(val.mag, mag));
resultMag = trustedStripLeadingZeroInts(resultMag);
return new BigInteger(resultMag, cmp == signum ? 1 : -1);
}
private static int[] subtract(int[] big, int[] little) {
int bigIndex = big.length;
int result[] = new int[bigIndex];
int littleIndex = little.length;
long difference = 0;
// Subtract common parts of both numbers
while (littleIndex > 0) {
difference = (big[--bigIndex] & LONG_MASK) -
(little[--littleIndex] & LONG_MASK) +
(difference >> 32);
result[bigIndex] = (int)difference;
}
// Subtract remainder of longer number while borrow propagates
boolean borrow = (difference >> 32 != 0);
while (bigIndex > 0 && borrow)
borrow = ((result[--bigIndex] = big[bigIndex] - 1) == -1);
// Copy remainder of longer number
while (bigIndex > 0)
result[--bigIndex] = big[bigIndex];
return result;
}
public BigInteger multiply(BigInteger val) {
if (val.signum == 0 || signum == 0)
return ZERO;
int xlen = mag.length;
if (val == this && xlen > MULTIPLY_SQUARE_THRESHOLD) {
return square();
}
int ylen = val.mag.length;
if ((xlen < KARATSUBA_THRESHOLD) || (ylen < KARATSUBA_THRESHOLD)) {
int resultSign = signum == val.signum ? 1 : -1;
if (val.mag.length == 1) {
return multiplyByInt(mag,val.mag[0], resultSign);
}
if (mag.length == 1) {
return multiplyByInt(val.mag,mag[0], resultSign);
}
int[] result = multiplyToLen(mag, xlen,
val.mag, ylen, null);
result = trustedStripLeadingZeroInts(result);
return new BigInteger(result, resultSign);
} else {
if ((xlen < TOOM_COOK_THRESHOLD) && (ylen < TOOM_COOK_THRESHOLD)) {
return multiplyKaratsuba(this, val);
} else {
return multiplyToomCook3(this, val);
}
}
}
private static BigInteger multiplyByInt(int[] x, int y, int sign) {
if (Integer.bitCount(y) == 1) {
return new BigInteger(shiftLeft(x,Integer.numberOfTrailingZeros(y)), sign);
}
int xlen = x.length;
int[] rmag = new int[xlen + 1];
long carry = 0;
long yl = y & LONG_MASK;
int rstart = rmag.length - 1;
for (int i = xlen - 1; i >= 0; i--) {
long product = (x[i] & LONG_MASK) * yl + carry;
rmag[rstart--] = (int)product;
carry = product >>> 32;
}
if (carry == 0L) {
rmag = java.util.Arrays.copyOfRange(rmag, 1, rmag.length);
} else {
rmag[rstart] = (int)carry;
}
return new BigInteger(rmag, sign);
}
BigInteger multiply(long v) {
if (v == 0 || signum == 0)
return ZERO;
if (v == BigDecimal.INFLATED)
return multiply(BigInteger.valueOf(v));
int rsign = (v > 0 ? signum : -signum);
if (v < 0)
v = -v;
long dh = v >>> 32; // higher order bits
long dl = v & LONG_MASK; // lower order bits
int xlen = mag.length;
int[] value = mag;
int[] rmag = (dh == 0L) ? (new int[xlen + 1]) : (new int[xlen + 2]);
long carry = 0;
int rstart = rmag.length - 1;
for (int i = xlen - 1; i >= 0; i--) {
long product = (value[i] & LONG_MASK) * dl + carry;
rmag[rstart--] = (int)product;
carry = product >>> 32;
}
rmag[rstart] = (int)carry;
if (dh != 0L) {
carry = 0;
rstart = rmag.length - 2;
for (int i = xlen - 1; i >= 0; i--) {
long product = (value[i] & LONG_MASK) * dh +
(rmag[rstart] & LONG_MASK) + carry;
rmag[rstart--] = (int)product;
carry = product >>> 32;
}
rmag[0] = (int)carry;
}
if (carry == 0L)
rmag = java.util.Arrays.copyOfRange(rmag, 1, rmag.length);
return new BigInteger(rmag, rsign);
}
private static int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
int xstart = xlen - 1;
int ystart = ylen - 1;
if (z == null || z.length < (xlen+ ylen))
z = new int[xlen+ylen];
long carry = 0;
for (int j=ystart, k=ystart+1+xstart; j >= 0; j--, k--) {
long product = (y[j] & LONG_MASK) *
(x[xstart] & LONG_MASK) + carry;
z[k] = (int)product;
carry = product >>> 32;
}
z[xstart] = (int)carry;
for (int i = xstart-1; i >= 0; i--) {
carry = 0;
for (int j=ystart, k=ystart+1+i; j >= 0; j--, k--) {
long product = (y[j] & LONG_MASK) *
(x[i] & LONG_MASK) +
(z[k] & LONG_MASK) + carry;
z[k] = (int)product;
carry = product >>> 32;
}
z[i] = (int)carry;
}
return z;
}
private static BigInteger multiplyKaratsuba(BigInteger x, BigInteger y) {
int xlen = x.mag.length;
int ylen = y.mag.length;
// The number of ints in each half of the number.
int half = (Math.max(xlen, ylen)+1) / 2;
// xl and yl are the lower halves of x and y respectively,
// xh and yh are the upper halves.
BigInteger xl = x.getLower(half);
BigInteger xh = x.getUpper(half);
BigInteger yl = y.getLower(half);
BigInteger yh = y.getUpper(half);
BigInteger p1 = xh.multiply(yh); // p1 = xh*yh
BigInteger p2 = xl.multiply(yl); // p2 = xl*yl
// p3=(xh+xl)*(yh+yl)
BigInteger p3 = xh.add(xl).multiply(yh.add(yl));
// result = p1 * 2^(32*2*half) + (p3 - p1 - p2) * 2^(32*half) + p2
BigInteger result = p1.shiftLeft(32*half).add(p3.subtract(p1).subtract(p2)).shiftLeft(32*half).add(p2);
if (x.signum != y.signum) {
return result.negate();
} else {
return result;
}
}
private static BigInteger multiplyToomCook3(BigInteger a, BigInteger b) {
int alen = a.mag.length;
int blen = b.mag.length;
int largest = Math.max(alen, blen);
// k is the size (in ints) of the lower-order slices.
int k = (largest+2)/3; // Equal to ceil(largest/3)
// r is the size (in ints) of the highest-order slice.
int r = largest - 2*k;
// Obtain slices of the numbers. a2 and b2 are the most significant
// bits of the numbers a and b, and a0 and b0 the least significant.
BigInteger a0, a1, a2, b0, b1, b2;
a2 = a.getToomSlice(k, r, 0, largest);
a1 = a.getToomSlice(k, r, 1, largest);
a0 = a.getToomSlice(k, r, 2, largest);
b2 = b.getToomSlice(k, r, 0, largest);
b1 = b.getToomSlice(k, r, 1, largest);
b0 = b.getToomSlice(k, r, 2, largest);
BigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1, db1;
v0 = a0.multiply(b0);
da1 = a2.add(a0);
db1 = b2.add(b0);
vm1 = da1.subtract(a1).multiply(db1.subtract(b1));
da1 = da1.add(a1);
db1 = db1.add(b1);
v1 = da1.multiply(db1);
v2 = da1.add(a2).shiftLeft(1).subtract(a0).multiply(
db1.add(b2).shiftLeft(1).subtract(b0));
vinf = a2.multiply(b2);
// The algorithm requires two divisions by 2 and one by 3.
// All divisions are known to be exact, that is, they do not produce
// remainders, and all results are positive. The divisions by 2 are
// implemented as right shifts which are relatively efficient, leaving
// only an exact division by 3, which is done by a specialized
// linear-time algorithm.
t2 = v2.subtract(vm1).exactDivideBy3();
tm1 = v1.subtract(vm1).shiftRight(1);
t1 = v1.subtract(v0);
t2 = t2.subtract(t1).shiftRight(1);
t1 = t1.subtract(tm1).subtract(vinf);
t2 = t2.subtract(vinf.shiftLeft(1));
tm1 = tm1.subtract(t2);
// Number of bits to shift left.
int ss = k*32;
BigInteger result = vinf.shiftLeft(ss).add(t2).shiftLeft(ss).add(t1).shiftLeft(ss).add(tm1).shiftLeft(ss).add(v0);
if (a.signum != b.signum) {
return result.negate();
} else {
return result;
}
}
private BigInteger getToomSlice(int lowerSize, int upperSize, int slice,
int fullsize) {
int start, end, sliceSize, len, offset;
len = mag.length;
offset = fullsize - len;
if (slice == 0) {
start = 0 - offset;
end = upperSize - 1 - offset;
} else {
start = upperSize + (slice-1)*lowerSize - offset;
end = start + lowerSize - 1;
}
if (start < 0) {
start = 0;
}
if (end < 0) {
return ZERO;
}
sliceSize = (end-start) + 1;
if (sliceSize = len) {
return this.abs();
}
int intSlice[] = new int[sliceSize];
System.arraycopy(mag, start, intSlice, 0, sliceSize);
return new BigInteger(trustedStripLeadingZeroInts(intSlice), 1);
}
private BigInteger exactDivideBy3() {
int len = mag.length;
int[] result = new int[len];
long x, w, q, borrow;
borrow = 0L;
for (int i=len-1; i >= 0; i--) {
x = (mag[i] & LONG_MASK);
w = x - borrow;
if (borrow > x) { // Did we make the number go negative?
borrow = 1L;
} else {
borrow = 0L;
}
// 0xAAAAAAAB is the modular inverse of 3 (mod 2^32). Thus,
// the effect of this is to divide by 3 (mod 2^32).
// This is much faster than division on most architectures.
q = (w * 0xAAAAAAABL) & LONG_MASK;
result[i] = (int) q;
// Now check the borrow. The second check can of course be
// eliminated if the first fails.
if (q >= 0x55555556L) {
borrow++;
if (q >= 0xAAAAAAABL)
borrow++;
}
}
result = trustedStripLeadingZeroInts(result);
return new BigInteger(result, signum);
}
BigDecimal
类的定义
public class BigDecimal extends Number implements Comparable {
private final BigInteger intVal;
private final int scale; // Note: this may have any value, so
// calculations must be done in longs
private transient int precision;
/**
* Used to store the canonical string representation, if computed.
*/
private transient String stringCache;
/**
* Sentinel value for {@link #intCompact} indicating the
* significand information is only available from {@code intVal}.
*/
static final long INFLATED = Long.MIN_VALUE;
private static final BigInteger INFLATED_BIGINT = BigInteger.valueOf(INFLATED);
/**
* If the absolute value of the significand of this BigDecimal is
* less than or equal to {@code Long.MAX_VALUE}, the value can be
* compactly stored in this field and used in computations.
*/
private final transient long intCompact;
// All 18-digit base ten strings fit into a long; not all 19-digit
// strings will
private static final int MAX_COMPACT_DIGITS = 18;
/* Appease the serialization gods */
private static final long serialVersionUID = 6108874887143696463L;
private static final ThreadLocal
threadLocalStringBuilderHelper = new ThreadLocal() {
@Override
protected StringBuilderHelper initialValue() {
return new StringBuilderHelper();
}
};
// Cache of common small BigDecimal values.
private static final BigDecimal zeroThroughTen[] = {
new BigDecimal(BigInteger.ZERO, 0, 0, 1),
new BigDecimal(BigInteger.ONE, 1, 0, 1),
new BigDecimal(BigInteger.valueOf(2), 2, 0, 1),
new BigDecimal(BigInteger.valueOf(3), 3, 0, 1),
new BigDecimal(BigInteger.valueOf(4), 4, 0, 1),
new BigDecimal(BigInteger.valueOf(5), 5, 0, 1),
new BigDecimal(BigInteger.valueOf(6), 6, 0, 1),
new BigDecimal(BigInteger.valueOf(7), 7, 0, 1),
new BigDecimal(BigInteger.valueOf(8), 8, 0, 1),
new BigDecimal(BigInteger.valueOf(9), 9, 0, 1),
new BigDecimal(BigInteger.TEN, 10, 0, 2),
};
// Cache of zero scaled by 0 - 15
private static final BigDecimal[] ZERO_SCALED_BY = {
zeroThroughTen[0],
new BigDecimal(BigInteger.ZERO, 0, 1, 1),
new BigDecimal(BigInteger.ZERO, 0, 2, 1),
new BigDecimal(BigInteger.ZERO, 0, 3, 1),
new BigDecimal(BigInteger.ZERO, 0, 4, 1),
new BigDecimal(BigInteger.ZERO, 0, 5, 1),
new BigDecimal(BigInteger.ZERO, 0, 6, 1),
new BigDecimal(BigInteger.ZERO, 0, 7, 1),
new BigDecimal(BigInteger.ZERO, 0, 8, 1),
new BigDecimal(BigInteger.ZERO, 0, 9, 1),
new BigDecimal(BigInteger.ZERO, 0, 10, 1),
new BigDecimal(BigInteger.ZERO, 0, 11, 1),
new BigDecimal(BigInteger.ZERO, 0, 12, 1),
new BigDecimal(BigInteger.ZERO, 0, 13, 1),
new BigDecimal(BigInteger.ZERO, 0, 14, 1),
new BigDecimal(BigInteger.ZERO, 0, 15, 1),
};
// Half of Long.MIN_VALUE & Long.MAX_VALUE.
private static final long HALF_LONG_MAX_VALUE = Long.MAX_VALUE / 2;
private static final long HALF_LONG_MIN_VALUE = Long.MIN_VALUE / 2;
// Constants
public static final BigDecimal ZERO =
zeroThroughTen[0];
public static final BigDecimal ONE =
zeroThroughTen[1];
public static final BigDecimal TEN =
zeroThroughTen[10];
// Constructors
BigDecimal(BigInteger intVal, long val, int scale, int prec) {
this.scale = scale;
this.precision = prec;
this.intCompact = val;
this.intVal = intVal;
}
public BigDecimal(char[] in, int offset, int len) {
this(in,offset,len,MathContext.UNLIMITED);
}
public BigDecimal(char[] in, int offset, int len, MathContext mc) {
// protect against huge length.
if (offset + len > in.length || offset < 0)
throw new NumberFormatException("Bad offset or len arguments for char[] input.");
// This is the primary string to BigDecimal constructor; all
// incoming strings end up here; it uses explicit (inline)
// parsing for speed and generates at most one intermediate
// (temporary) object (a char[] array) for non-compact case.
// Use locals for all fields values until completion
int prec = 0; // record precision value
int scl = 0; // record scale value
long rs = 0; // the compact value in long
BigInteger rb = null; // the inflated value in BigInteger
// use array bounds checking to handle too-long, len == 0,
// bad offset, etc.
try {
// handle the sign
boolean isneg = false; // assume positive
if (in[offset] == '-') {
isneg = true; // leading minus means negative
offset++;
len--;
} else if (in[offset] == '+') { // leading + allowed
offset++;
len--;
}
// should now be at numeric part of the significand
boolean dot = false; // true when there is a '.'
long exp = 0; // exponent
char c; // current character
boolean isCompact = (len 0; offset++, len--) {
c = in[offset];
if ((c == '0')) { // have zero
if (prec == 0)
prec = 1;
else if (rs != 0) {
rs *= 10;
++prec;
} // else digit is a redundant leading zero
if (dot)
++scl;
} else if ((c >= '1' && c 0 && drop > 0) { // do rounding
while (drop > 0) {
scl = checkScaleNonZero((long) scl - drop);
rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
prec = longDigitLength(rs);
drop = prec - mcp;
}
}
} else {
char coeff[] = new char[len];
for (; len > 0; offset++, len--) {
c = in[offset];
// have digit
if ((c >= '0' && c 0 && (prec > mcp)) {
if (rs == INFLATED) {
int drop = prec - mcp;
while (drop > 0) {
scl = checkScaleNonZero((long) scl - drop);
rb = divideAndRoundByTenPow(rb, drop, mc.roundingMode.oldMode);
rs = compactValFor(rb);
if (rs != INFLATED) {
prec = longDigitLength(rs);
break;
}
prec = bigDigitLength(rb);
drop = prec - mcp;
}
}
if (rs != INFLATED) {
int drop = prec - mcp;
while (drop > 0) {
scl = checkScaleNonZero((long) scl - drop);
rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
prec = longDigitLength(rs);
drop = prec - mcp;
}
rb = null;
}
}
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new NumberFormatException();
} catch (NegativeArraySizeException e) {
throw new NumberFormatException();
}
this.scale = scl;
this.precision = prec;
this.intCompact = rs;
this.intVal = rb;
}
private int adjustScale(int scl, long exp) {
long adjustedScale = scl - exp;
if (adjustedScale > Integer.MAX_VALUE || adjustedScale < Integer.MIN_VALUE)
throw new NumberFormatException("Scale out of range.");
scl = (int) adjustedScale;
return scl;
}
private static long parseExp(char[] in, int offset, int len){
long exp = 0;
offset++;
char c = in[offset];
len--;
boolean negexp = (c == '-');
// optional sign
if (negexp || c == '+') {
offset++;
c = in[offset];
len--;
}
if (len 10 && (c=='0' || (Character.digit(c, 10) == 0))) {
offset++;
c = in[offset];
len--;
}
if (len > 10) // too many nonzero exponent digits
throw new NumberFormatException();
// c now holds first digit of exponent
for (;; len--) {
int v;
if (c >= '0' && c > 63) == 0 ? 1 : -1);
int exponent = (int) ((valBits >> 52) & 0x7ffL);
long significand = (exponent == 0
? (valBits & ((1L 0) { // do rounding
int mode = mc.roundingMode.oldMode;
int drop;
if (compactVal == INFLATED) {
prec = bigDigitLength(intVal);
drop = prec - mcp;
while (drop > 0) {
scale = checkScaleNonZero((long) scale - drop);
intVal = divideAndRoundByTenPow(intVal, drop, mode);
compactVal = compactValFor(intVal);
if (compactVal != INFLATED) {
break;
}
prec = bigDigitLength(intVal);
drop = prec - mcp;
}
}
if (compactVal != INFLATED) {
prec = longDigitLength(compactVal);
drop = prec - mcp;
while (drop > 0) {
scale = checkScaleNonZero((long) scale - drop);
compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
prec = longDigitLength(compactVal);
drop = prec - mcp;
}
intVal = null;
}
}
this.intVal = intVal;
this.intCompact = compactVal;
this.scale = scale;
this.precision = prec;
}
public BigDecimal(BigInteger val) {
scale = 0;
intVal = val;
intCompact = compactValFor(val);
}
public BigDecimal(BigInteger val, MathContext mc) {
this(val,0,mc);
}
public BigDecimal(BigInteger unscaledVal, int scale) {
// Negative scales are now allowed
this.intVal = unscaledVal;
this.intCompact = compactValFor(unscaledVal);
this.scale = scale;
}
public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) {
long compactVal = compactValFor(unscaledVal);
int mcp = mc.precision;
int prec = 0;
if (mcp > 0) { // do rounding
int mode = mc.roundingMode.oldMode;
if (compactVal == INFLATED) {
prec = bigDigitLength(unscaledVal);
int drop = prec - mcp;
while (drop > 0) {
scale = checkScaleNonZero((long) scale - drop);
unscaledVal = divideAndRoundByTenPow(unscaledVal, drop, mode);
compactVal = compactValFor(unscaledVal);
if (compactVal != INFLATED) {
break;
}
prec = bigDigitLength(unscaledVal);
drop = prec - mcp;
}
}
if (compactVal != INFLATED) {
prec = longDigitLength(compactVal);
int drop = prec - mcp; // drop can't be more than 18
while (drop > 0) {
scale = checkScaleNonZero((long) scale - drop);
compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mode);
prec = longDigitLength(compactVal);
drop = prec - mcp;
}
unscaledVal = null;
}
}
this.intVal = unscaledVal;
this.intCompact = compactVal;
this.scale = scale;
this.precision = prec;
}
public BigDecimal(int val) {
this.intCompact = val;
this.scale = 0;
this.intVal = null;
}
public BigDecimal(int val, MathContext mc) {
int mcp = mc.precision;
long compactVal = val;
int scale = 0;
int prec = 0;
if (mcp > 0) { // do rounding
prec = longDigitLength(compactVal);
int drop = prec - mcp; // drop can't be more than 18
while (drop > 0) {
scale = checkScaleNonZero((long) scale - drop);
compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
prec = longDigitLength(compactVal);
drop = prec - mcp;
}
}
this.intVal = null;
this.intCompact = compactVal;
this.scale = scale;
this.precision = prec;
}
public BigDecimal(long val) {
this.intCompact = val;
this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
this.scale = 0;
}
public BigDecimal(long val, MathContext mc) {
int mcp = mc.precision;
int mode = mc.roundingMode.oldMode;
int prec = 0;
int scale = 0;
BigInteger intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
if (mcp > 0) { // do rounding
if (val == INFLATED) {
prec = 19;
int drop = prec - mcp;
while (drop > 0) {
scale = checkScaleNonZero((long) scale - drop);
intVal = divideAndRoundByTenPow(intVal, drop, mode);
val = compactValFor(intVal);
if (val != INFLATED) {
break;
}
prec = bigDigitLength(intVal);
drop = prec - mcp;
}
}
if (val != INFLATED) {
prec = longDigitLength(val);
int drop = prec - mcp;
while (drop > 0) {
scale = checkScaleNonZero((long) scale - drop);
val = divideAndRound(val, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
prec = longDigitLength(val);
drop = prec - mcp;
}
intVal = null;
}
}
this.intVal = intVal;
this.intCompact = val;
this.scale = scale;
this.precision = prec;
}
// Static Factory Methods
public static BigDecimal valueOf(long unscaledVal, int scale) {
if (scale == 0)
return valueOf(unscaledVal);
else if (unscaledVal == 0) {
return zeroValueOf(scale);
}
return new BigDecimal(unscaledVal == INFLATED ?
INFLATED_BIGINT : null,
unscaledVal, scale, 0);
}
public static BigDecimal valueOf(long val) {
if (val >= 0 && val < zeroThroughTen.length)
return zeroThroughTen[(int)val];
else if (val != INFLATED)
return new BigDecimal(null, val, 0, 0);
return new BigDecimal(INFLATED_BIGINT, val, 0, 0);
}
static BigDecimal valueOf(long unscaledVal, int scale, int prec) {
if (scale == 0 && unscaledVal >= 0 && unscaledVal < zeroThroughTen.length) {
return zeroThroughTen[(int) unscaledVal];
} else if (unscaledVal == 0) {
return zeroValueOf(scale);
}
return new BigDecimal(unscaledVal == INFLATED ? INFLATED_BIGINT : null,
unscaledVal, scale, prec);
}
static BigDecimal valueOf(BigInteger intVal, int scale, int prec) {
long val = compactValFor(intVal);
if (val == 0) {
return zeroValueOf(scale);
} else if (scale == 0 && val >= 0 && val < zeroThroughTen.length) {
return zeroThroughTen[(int) val];
}
return new BigDecimal(intVal, val, scale, prec);
}
static BigDecimal zeroValueOf(int scale) {
if (scale >= 0 && scale < ZERO_SCALED_BY.length)
return ZERO_SCALED_BY[scale];
else
return new BigDecimal(BigInteger.ZERO, 0, scale, 1);
}
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
和BigInteger
类似,BigDecimal
可以表示一个任意大小且精度完全准确的浮点数。
scale
BigDecimal
用scale()
表示小数位数,例如:
BigDecimal d1 = new BigDecimal("123.45");
BigDecimal d2 = new BigDecimal("123.4500");
BigDecimal d3 = new BigDecimal("1234500");
System.out.println(d1.scale()); // 2,两位小数
System.out.println(d2.scale()); // 4
System.out.println(d3.scale()); // 0
通过BigDecimal
的stripTrailingZeros()
方法,可以将一个BigDecimal
格式化为一个相等的,但去掉了末尾0的BigDecimal
:
BigDecimal d1 = new BigDecimal("123.4500");
BigDecimal d2 = d1.stripTrailingZeros();
System.out.println(d1.scale()); // 4
System.out.println(d2.scale()); // 2,因为去掉了00
BigDecimal d3 = new BigDecimal("1234500");
BigDecimal d4 = d1.stripTrailingZeros();
System.out.println(d3.scale()); // 0
System.out.println(d4.scale()); // -2
如果一个BigDecimal
的scale()
返回负数,例如,-2
,表示这个数是个整数,并且末尾有2个0。
可以对一个BigDecimal
设置它的scale
,如果精度比原始值低,那么按照指定的方法进行四舍五入或者直接截断:
BigDecimal d1 = new BigDecimal("123.456789");
BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP); // 四舍五入,123.4568
BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN); // 直接截断,123.4567
public BigDecimal add(BigDecimal augend) {
if (this.intCompact != INFLATED) {
if ((augend.intCompact != INFLATED)) {
return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
} else {
return add(this.intCompact, this.scale, augend.intVal, augend.scale);
}
} else {
if ((augend.intCompact != INFLATED)) {
return add(augend.intCompact, augend.scale, this.intVal, this.scale);
} else {
return add(this.intVal, this.scale, augend.intVal, augend.scale);
}
}
}
public BigDecimal add(BigDecimal augend, MathContext mc) {
if (mc.precision == 0)
return add(augend);
BigDecimal lhs = this;
// If either number is zero then the other number, rounded and
// scaled if necessary, is used as the result.
{
boolean lhsIsZero = lhs.signum() == 0;
boolean augendIsZero = augend.signum() == 0;
if (lhsIsZero || augendIsZero) {
int preferredScale = Math.max(lhs.scale(), augend.scale());
BigDecimal result;
if (lhsIsZero && augendIsZero)
return zeroValueOf(preferredScale);
result = lhsIsZero ? doRound(augend, mc) : doRound(lhs, mc);
if (result.scale() == preferredScale)
return result;
else if (result.scale() > preferredScale) {
return stripZerosToMatchScale(result.intVal, result.intCompact, result.scale, preferredScale);
} else { // result.scale < preferredScale
int precisionDiff = mc.precision - result.precision();
int scaleDiff = preferredScale - result.scale();
if (precisionDiff >= scaleDiff)
return result.setScale(preferredScale); // can achieve target scale
else
return result.setScale(result.scale() + precisionDiff);
}
}
}
long padding = (long) lhs.scale - augend.scale;
if (padding != 0) { // scales differ; alignment needed
BigDecimal arg[] = preAlign(lhs, augend, padding, mc);
matchScale(arg);
lhs = arg[0];
augend = arg[1];
}
return doRound(lhs.inflated().add(augend.inflated()), lhs.scale, mc);
}
private BigDecimal[] preAlign(BigDecimal lhs, BigDecimal augend, long padding, MathContext mc) {
assert padding != 0;
BigDecimal big;
BigDecimal small;
if (padding < 0) { // lhs is big; augend is small
big = lhs;
small = augend;
} else { // lhs is small; augend is big
big = augend;
small = lhs;
}
/*
* This is the estimated scale of an ulp of the result; it assumes that
* the result doesn't have a carry-out on a true add (e.g. 999 + 1 =>
* 1000) or any subtractive cancellation on borrowing (e.g. 100 - 1.2 =>
* 98.8)
*/
long estResultUlpScale = (long) big.scale - big.precision() + mc.precision;
long smallHighDigitPos = (long) small.scale - small.precision() + 1;
if (smallHighDigitPos > big.scale + 2 && // big and small disjoint
smallHighDigitPos > estResultUlpScale + 2) { // small digits not visible
small = BigDecimal.valueOf(small.signum(), this.checkScale(Math.max(big.scale, estResultUlpScale) + 3));
}
// Since addition is symmetric, preserving input order in
// returned operands doesn't matter
BigDecimal[] result = {big, small};
return result;
}
public BigDecimal subtract(BigDecimal subtrahend) {
if (this.intCompact != INFLATED) {
if ((subtrahend.intCompact != INFLATED)) {
return add(this.intCompact, this.scale, -subtrahend.intCompact, subtrahend.scale);
} else {
return add(this.intCompact, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
}
} else {
if ((subtrahend.intCompact != INFLATED)) {
// Pair of subtrahend values given before pair of
// values from this BigDecimal to avoid need for
// method overloading on the specialized add method
return add(-subtrahend.intCompact, subtrahend.scale, this.intVal, this.scale);
} else {
return add(this.intVal, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
}
}
}
public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) {
if (mc.precision == 0)
return subtract(subtrahend);
// share the special rounding code in add()
return add(subtrahend.negate(), mc);
}
public BigDecimal multiply(BigDecimal multiplicand) {
int productScale = checkScale((long) scale + multiplicand.scale);
if (this.intCompact != INFLATED) {
if ((multiplicand.intCompact != INFLATED)) {
return multiply(this.intCompact, multiplicand.intCompact, productScale);
} else {
return multiply(this.intCompact, multiplicand.intVal, productScale);
}
} else {
if ((multiplicand.intCompact != INFLATED)) {
return multiply(multiplicand.intCompact, this.intVal, productScale);
} else {
return multiply(this.intVal, multiplicand.intVal, productScale);
}
}
}
public BigDecimal multiply(BigDecimal multiplicand, MathContext mc) {
if (mc.precision == 0)
return multiply(multiplicand);
int productScale = checkScale((long) scale + multiplicand.scale);
if (this.intCompact != INFLATED) {
if ((multiplicand.intCompact != INFLATED)) {
return multiplyAndRound(this.intCompact, multiplicand.intCompact, productScale, mc);
} else {
return multiplyAndRound(this.intCompact, multiplicand.intVal, productScale, mc);
}
} else {
if ((multiplicand.intCompact != INFLATED)) {
return multiplyAndRound(multiplicand.intCompact, this.intVal, productScale, mc);
} else {
return multiplyAndRound(this.intVal, multiplicand.intVal, productScale, mc);
}
}
}
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
throw new IllegalArgumentException("Invalid rounding mode");
if (this.intCompact != INFLATED) {
if ((divisor.intCompact != INFLATED)) {
return divide(this.intCompact, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
} else {
return divide(this.intCompact, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
}
} else {
if ((divisor.intCompact != INFLATED)) {
return divide(this.intVal, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
} else {
return divide(this.intVal, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
}
}
}
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
return divide(divisor, scale, roundingMode.oldMode);
}
public BigDecimal divide(BigDecimal divisor, int roundingMode) {
return this.divide(divisor, scale, roundingMode);
}
public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) {
return this.divide(divisor, scale, roundingMode.oldMode);
}
public BigDecimal divide(BigDecimal divisor) {
if (divisor.signum() == 0) { // x/0
if (this.signum() == 0) // 0/0
throw new ArithmeticException("Division undefined"); // NaN
throw new ArithmeticException("Division by zero");
}
// Calculate preferred scale
int preferredScale = saturateLong((long) this.scale - divisor.scale);
if (this.signum() == 0) // 0/y
return zeroValueOf(preferredScale);
else {
/*
* If the quotient this/divisor has a terminating decimal
* expansion, the expansion can have no more than
* (a.precision() + ceil(10*b.precision)/3) digits.
* Therefore, create a MathContext object with this
* precision and do a divide with the UNNECESSARY rounding
* mode.
*/
MathContext mc = new MathContext( (int)Math.min(this.precision() +
(long)Math.ceil(10.0*divisor.precision()/3.0),
Integer.MAX_VALUE),
RoundingMode.UNNECESSARY);
BigDecimal quotient;
try {
quotient = this.divide(divisor, mc);
} catch (ArithmeticException e) {
throw new ArithmeticException("Non-terminating decimal expansion; " +
"no exact representable decimal result.");
}
int quotientScale = quotient.scale();
// divide(BigDecimal, mc) tries to adjust the quotient to
// the desired one by removing trailing zeros; since the
// exact divide method does not have an explicit digit
// limit, we can add zeros too.
if (preferredScale > quotientScale)
return quotient.setScale(preferredScale, ROUND_UNNECESSARY);
return quotient;
}
}
public BigDecimal divide(BigDecimal divisor, MathContext mc) {
int mcp = mc.precision;
if (mcp == 0)
return divide(divisor);
BigDecimal dividend = this;
long preferredScale = (long)dividend.scale - divisor.scale;
// Now calculate the answer. We use the existing
// divide-and-round method, but as this rounds to scale we have
// to normalize the values here to achieve the desired result.
// For x/y we first handle y=0 and x=0, and then normalize x and
// y to give x' and y' with the following constraints:
// (a) 0.1 0) {
result = result.setScale(0, RoundingMode.DOWN);
}
// else result.scale() == 0;
int precisionDiff;
if ((preferredScale > result.scale()) &&
(precisionDiff = mc.precision - result.precision()) > 0) {
return result.setScale(result.scale() +
Math.min(precisionDiff, preferredScale - result.scale) );
} else {
return stripZerosToMatchScale(result.intVal,result.intCompact,result.scale,preferredScale);
}
}
public BigDecimal remainder(BigDecimal divisor) {
BigDecimal divrem[] = this.divideAndRemainder(divisor);
return divrem[1];
}
public BigDecimal remainder(BigDecimal divisor, MathContext mc) {
BigDecimal divrem[] = this.divideAndRemainder(divisor, mc);
return divrem[1];
}
对BigDecimal
做加、减、乘时,精度不会丢失,但是做除法时,存在无法除尽的情况,这时,就必须指定精度以及如何进行截断:
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小数并四舍五入
在比较两个BigDecimal
的值是否相等时,要特别注意,使用equals()
方法不但要求两个BigDecimal
的值相等,还要求它们的scale()
相等:
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("123.45600");
System.out.println(d1.equals(d2)); // false,因为scale不同
System.out.println(d1.equals(d2.stripTrailingZeros())); // true,因为d2去除尾部0后scale变为2
System.out.println(d1.compareTo(d2)); // 0
必须使用compareTo()
方法来比较,它根据两个值的大小分别返回负数、正数和0
,分别表示小于、大于和等于。
总是使用compareTo()比较两个BigDecimal的值,不要使用equals()!