Java 工具类总结(4): 保证精度的运算 - BigInteger / BigDecimal

如果基本类型 long / double 不能满足整数和浮点数运算的 精度要求, 则可以使用 java.math 包 下的 BigInteger / BigDecimal 工具类, 实现任何精度的运算;

使用 BigInteger 实现了任意精度的整型运算
使用 BigDecimal 实现了任意精度的浮点型运算

1. 基本用法与常用方法

/*
 * 以 BigInteger 为例 (BigDecimal 方法与 BigInteger 相同)
 */
public static void main(String[] args) {
    // 1. 构造实例:
    // 两种方法: 通过静态方法 valueOf() 和 new Instance 方法
    BigInteger bi = BigInteger.valueOf(3_123_456_789L);
    BigInteger bi2 = new BigInteger("2333333333333333");    
        // BigInteger(byte[])
        // BigInteger(int[])
        // BigInteger(int, byte[])
        // BigInteger(int, int[])
        // BigInteger(String, int)
        // BigInteger(char[], int, int)

    // 2. add / sub / multiply /  divide / mod
    // 在 BigInteger 和 BigDecimal 中, 没有算数运算符, 只能通过实例方法进行运算
    // 注意: 在使用实例方法运算的时候, 如: 使用 bi.add(bi2), 不会改变 bi 和 bi2 的值;
    System.out.println(bi.add(bi2));;
    System.out.println(bi.subtract(bi2));
    System.out.println(bi.multiply(bi2));
    System.out.println(bi.divide(bi2));
    System.out.println(bi.mod(bi2));

}

2. 源码分析

2.1 BigInteger 源码

/**
 * Immutable arbitrary-precision (任意精度) integers.  All operations behave as if
 * BigIntegers were represented in two's-complement notation (like Java's
 * primitive integer types).  BigInteger provides analogues(类似物, 这里指的是实例方法, 
 * 如: add() / substract() 方法, 替代 + / - 运算符) to all of Java's
 * primitive integer operators, and all relevant methods from java.lang.Math.
 * Additionally, BigInteger provides operations for modular arithmetic, GCD
 * calculation, primality testing, prime generation, bit manipulation,
 * and a few other miscellaneous operations.
 *
 * @see     BigDecimal
 * @since JDK1.1
 */

public class BigInteger extends Number implements Comparable<BigInteger> {
    /**
     * The signum of this BigInteger: -1 for negative, 0 for zero, or
     * 1 for positive.  Note that the BigInteger zero must have
     * a signum of 0.  This is necessary to ensures that there is exactly one
     * representation for each BigInteger value.
     *
     * @serial
     */
    final int signum;

    /**
     * The magnitude of this BigInteger, in big-endian order: the
     * zeroth element of this array is the most-significant int of the
     * magnitude.  The magnitude must be "minimal" in that the most-significant
     * int ({@code mag[0]}) must be non-zero.  This is necessary to
     * ensure that there is exactly one representation for each BigInteger
     * value.  Note that this implies that the BigInteger zero has a
     * zero-length mag array.
     */
    final int[] mag;

    /**
     * Translates a byte array containing the two's-complement binary
     * representation of a BigInteger into a BigInteger.  The input array is
     * assumed to be in big-endian byte-order: the most significant
     * byte is in the zeroth element.
     *
     * @param  val big-endian two's-complement binary representation of
     *         BigInteger.
     * @throws NumberFormatException {@code val} is zero bytes long.
     */
    public BigInteger(byte[] val) {
       // ...
    }

    /**
     * This private constructor translates an int array containing the
     * two's-complement binary representation of a BigInteger into a
     * BigInteger. The input array is assumed to be in big-endian
     * int-order: the most significant int is in the zeroth element.
     */
    private BigInteger(int[] val) {
        //... 
    }

    /**
     * Translates the sign-magnitude representation of a BigInteger into a
     * BigInteger.  The sign is represented as an integer signum value: -1 for
     * negative, 0 for zero, or 1 for positive.  The magnitude is a byte array
     * in big-endian byte-order: the most significant byte is in the
     * zeroth element.  A zero-length magnitude array is permissible, and will
     * result in a BigInteger value of 0, whether signum is -1, 0 or 1.
     *
     * @param  signum signum of the number (-1 for negative, 0 for zero, 1
     *         for positive).
     * @param  magnitude big-endian binary representation of the magnitude of
     *         the number.
     * @throws NumberFormatException {@code signum} is not one of the three
     *         legal values (-1, 0, and 1), or {@code signum} is 0 and
     *         {@code magnitude} contains one or more non-zero bytes.
     */
    public BigInteger(int signum, byte[] magnitude) {
        //... 
    }

    /**
     * A constructor for internal use that translates the sign-magnitude
     * representation of a BigInteger into a BigInteger. It checks the
     * arguments and copies the magnitude so this constructor would be
     * safe for external use.
     */
    private BigInteger(int signum, int[] magnitude) {
        // ...
    }

    /**
     * Translates the String representation of a BigInteger in the
     * specified radix into a BigInteger.  The String representation
     * consists of an optional minus or plus sign followed by a
     * sequence of one or more digits in the specified radix.  The
     * character-to-digit mapping is provided by {@code
     * Character.digit}.  The String may not contain any extraneous
     * characters (whitespace, for example).
     *
     * @param val String representation of BigInteger.
     * @param radix radix to be used in interpreting {@code val}.
     * @throws NumberFormatException {@code val} is not a valid representation
     *         of a BigInteger in the specified radix, or {@code radix} is
     *         outside the range from {@link Character#MIN_RADIX} to
     *         {@link Character#MAX_RADIX}, inclusive.
     * @see    Character#digit
     */
    public BigInteger(String val, int radix) {
        //... 
    }

    /*
     * Constructs a new BigInteger using a char array with radix=10.
     * Sign is precalculated outside and not allowed in the val.
     */
    BigInteger(char[] val, int sign, int len) {
       // ...
}


2.2 BigDecimal 源码

/**
 * Immutable, arbitrary-precision signed decimal numbers.  A
 * {@code BigDecimal} consists of an arbitrary precision integer
 * unscaled value and a 32-bit integer scale.  If zero
 * or positive, the scale is the number of digits to the right of the
 * decimal point.  If negative, the unscaled value of the number is
 * multiplied by ten to the power of the negation of the scale.  The
 * value of the number represented by the {@code BigDecimal} is
 * therefore (unscaledValue × 10-scale).
 * 不可变的, 可以执行任何精度运算的, 带符号的浮点运算工具类: BigDecimal
 * 
 * @see     BigInteger
 */
public class BigDecimal extends Number implements Comparable<BigDecimal> {
    /**
     * The unscaled value of this BigDecimal, as returned by {@link
     * #unscaledValue}.
     *
     * @serial
     * @see #unscaledValue
     */
    private final BigInteger intVal;

    /**
     * The scale of this BigDecimal, as returned by {@link #scale}.
     *
     * @serial
     * @see #scale
     */
    private final int scale;  // Note: this may have any value, so
                              // calculations must be done in longs

    /**
     * The number of decimal digits in this BigDecimal, or 0 if the
     * number of digits are not known (lookaside information).  If
     * nonzero, the value is guaranteed correct.  Use the precision()
     * method to obtain and set the value if it might be 0.  This
     * field is mutable until set nonzero.
     *
     * @since  1.5
     */
    private transient int precision;

    /**
     * Used to store the canonical string representation, if computed.
     */
    private transient String stringCache;


    /**
     * Trusted package private constructor.
     * Trusted simply means if val is INFLATED, intVal could not be null and
     * if intVal is null, val could not be INFLATED.
     */
    BigDecimal(BigInteger intVal, long val, int scale, int prec) {
        this.scale = scale;
        this.precision = prec;
        this.intCompact = val;
        this.intVal = intVal;
    }

    /**
     * Translates a character array representation of a
     * {@code BigDecimal} into a {@code BigDecimal}, accepting the
     * same sequence of characters as the {@link #BigDecimal(String)}
     * constructor, while allowing a sub-array to be specified.
     *
     * 

Note that if the sequence of characters is already available * within a character array, using this constructor is faster than * converting the {@code char} array to string and using the * {@code BigDecimal(String)} constructor . * * @param in {@code char} array that is the source of characters. * @param offset first character in the array to inspect. * @param len number of characters to consider. * @throws NumberFormatException if {@code in} is not a valid * representation of a {@code BigDecimal} or the defined subarray * is not wholly within {@code in}. * @since 1.5 */ public BigDecimal(char[] in, int offset, int len) { this(in,offset,len,MathContext.UNLIMITED); } /** * Translates a character array representation of a * {@code BigDecimal} into a {@code BigDecimal}, accepting the * same sequence of characters as the {@link #BigDecimal(String)} * constructor, while allowing a sub-array to be specified and * with rounding according to the context settings. * *

Note that if the sequence of characters is already available * within a character array, using this constructor is faster than * converting the {@code char} array to string and using the * {@code BigDecimal(String)} constructor . * * @param in {@code char} array that is the source of characters. * @param offset first character in the array to inspect. * @param len number of characters to consider.. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * rounding mode is {@code UNNECESSARY}. * @throws NumberFormatException if {@code in} is not a valid * representation of a {@code BigDecimal} or the defined subarray * is not wholly within {@code in}. * @since 1.5 */ public BigDecimal(char[] in, int offset, int len, MathContext mc) { // ... } /* * parse exponent */ private static long parseExp(char[] in, int offset, int len){ // .... } /** * Translates a character array representation of a * {@code BigDecimal} into a {@code BigDecimal}, accepting the * same sequence of characters as the {@link #BigDecimal(String)} * constructor. * *

Note that if the sequence of characters is already available * as a character array, using this constructor is faster than * converting the {@code char} array to string and using the * {@code BigDecimal(String)} constructor . * * @param in {@code char} array that is the source of characters. * @throws NumberFormatException if {@code in} is not a valid * representation of a {@code BigDecimal}. * @since 1.5 */ public BigDecimal(char[] in) { this(in, 0, in.length); } /** * Translates a character array representation of a * {@code BigDecimal} into a {@code BigDecimal}, accepting the * same sequence of characters as the {@link #BigDecimal(String)} * constructor and with rounding according to the context * settings. * *

Note that if the sequence of characters is already available * as a character array, using this constructor is faster than * converting the {@code char} array to string and using the * {@code BigDecimal(String)} constructor . * * @param in {@code char} array that is the source of characters. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * rounding mode is {@code UNNECESSARY}. * @throws NumberFormatException if {@code in} is not a valid * representation of a {@code BigDecimal}. * @since 1.5 */ public BigDecimal(char[] in, MathContext mc) { this(in, 0, in.length, mc); } /** * Translates the string representation of a {@code BigDecimal} * into a {@code BigDecimal}. The string representation consists * of an optional sign, {@code '+'} ( '\u002B') or * {@code '-'} ('\u002D'), followed by a sequence of * zero or more decimal digits ("the integer"), optionally * followed by a fraction, optionally followed by an exponent. * *

The fraction consists of a decimal point followed by zero * or more decimal digits. The string must contain at least one * digit in either the integer or the fraction. The number formed * by the sign, the integer and the fraction is referred to as the * significand. * *

The exponent consists of the character {@code 'e'} * ('\u0065') or {@code 'E'} ('\u0045') * followed by one or more decimal digits. The value of the * exponent must lie between -{@link Integer#MAX_VALUE} ({@link * Integer#MIN_VALUE}+1) and {@link Integer#MAX_VALUE}, inclusive. * *

More formally, the strings this constructor accepts are * described by the following grammar: *

*
*
BigDecimalString: *
Signopt Significand Exponentopt *
Sign: *
{@code +} *
{@code -} *
Significand: *
IntegerPart {@code .} FractionPartopt *
{@code .} FractionPart *
IntegerPart *
IntegerPart: *
Digits *
FractionPart: *
Digits *
Exponent: *
ExponentIndicator SignedInteger *
ExponentIndicator: *
{@code e} *
{@code E} *
SignedInteger: *
Signopt Digits *
Digits: *
Digit *
Digits Digit *
Digit: *
any character for which {@link Character#isDigit} * returns {@code true}, including 0, 1, 2 ... *
*
* *

The scale of the returned {@code BigDecimal} will be the * number of digits in the fraction, or zero if the string * contains no decimal point, subject to adjustment for any * exponent; if the string contains an exponent, the exponent is * subtracted from the scale. The value of the resulting scale * must lie between {@code Integer.MIN_VALUE} and * {@code Integer.MAX_VALUE}, inclusive. * *

The character-to-digit mapping is provided by {@link * java.lang.Character#digit} set to convert to radix 10. The * String may not contain any extraneous characters (whitespace, * for example). * *

Examples:
* The value of the returned {@code BigDecimal} is equal to * significand × 10 exponent. * For each string on the left, the resulting representation * [{@code BigInteger}, {@code scale}] is shown on the right. *

     * "0"            [0,0]
     * "0.00"         [0,2]
     * "123"          [123,0]
     * "-123"         [-123,0]
     * "1.23E3"       [123,-1]
     * "1.23E+3"      [123,-1]
     * "12.3E+7"      [123,-6]
     * "12.0"         [120,1]
     * "12.3"         [123,1]
     * "0.00123"      [123,5]
     * "-1.23E-12"    [-123,14]
     * "1234.5E-4"    [12345,5]
     * "0E+7"         [0,-7]
     * "-0"           [0,0]
     * 
* *

Note: For values other than {@code float} and * {@code double} NaN and ±Infinity, this constructor is * compatible with the values returned by {@link Float#toString} * and {@link Double#toString}. This is generally the preferred * way to convert a {@code float} or {@code double} into a * BigDecimal, as it doesn't suffer from the unpredictability of * the {@link #BigDecimal(double)} constructor. * * @param val String representation of {@code BigDecimal}. * * @throws NumberFormatException if {@code val} is not a valid * representation of a {@code BigDecimal}. */ public BigDecimal(String val) { this(val.toCharArray(), 0, val.length()); } /** * Translates the string representation of a {@code BigDecimal} * into a {@code BigDecimal}, accepting the same strings as the * {@link #BigDecimal(String)} constructor, with rounding * according to the context settings. * * @param val string representation of a {@code BigDecimal}. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * rounding mode is {@code UNNECESSARY}. * @throws NumberFormatException if {@code val} is not a valid * representation of a BigDecimal. * @since 1.5 */ public BigDecimal(String val, MathContext mc) { this(val.toCharArray(), 0, val.length(), mc); } /** * Translates a {@code double} into a {@code BigDecimal} which * is the exact decimal representation of the {@code double}'s * binary floating-point value. The scale of the returned * {@code BigDecimal} is the smallest value such that * (10scale × val) is an integer. *

* Notes: *

    *
  1. * The results of this constructor can be somewhat unpredictable. * One might assume that writing {@code new BigDecimal(0.1)} in * Java creates a {@code BigDecimal} which is exactly equal to * 0.1 (an unscaled value of 1, with a scale of 1), but it is * actually equal to * 0.1000000000000000055511151231257827021181583404541015625. * This is because 0.1 cannot be represented exactly as a * {@code double} (or, for that matter, as a binary fraction of * any finite length). Thus, the value that is being passed * in to the constructor is not exactly equal to 0.1, * appearances notwithstanding. * *
  2. * The {@code String} constructor, on the other hand, is * perfectly predictable: writing {@code new BigDecimal("0.1")} * creates a {@code BigDecimal} which is exactly equal to * 0.1, as one would expect. Therefore, it is generally * recommended that the {@linkplain #BigDecimal(String) * String constructor} be used in preference to this one. * *
  3. * When a {@code double} must be used as a source for a * {@code BigDecimal}, note that this constructor provides an * exact conversion; it does not give the same result as * converting the {@code double} to a {@code String} using the * {@link Double#toString(double)} method and then using the * {@link #BigDecimal(String)} constructor. To get that result, * use the {@code static} {@link #valueOf(double)} method. *
* * @param val {@code double} value to be converted to * {@code BigDecimal}. * @throws NumberFormatException if {@code val} is infinite or NaN. */
public BigDecimal(double val) { this(val,MathContext.UNLIMITED); } /** * Translates a {@code double} into a {@code BigDecimal}, with * rounding according to the context settings. The scale of the * {@code BigDecimal} is the smallest value such that * (10scale × val) is an integer. * *

The results of this constructor can be somewhat unpredictable * and its use is generally not recommended; see the notes under * the {@link #BigDecimal(double)} constructor. * * @param val {@code double} value to be converted to * {@code BigDecimal}. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * RoundingMode is UNNECESSARY. * @throws NumberFormatException if {@code val} is infinite or NaN. * @since 1.5 */ public BigDecimal(double val, MathContext mc) { // ... } /** * Translates a {@code BigInteger} into a {@code BigDecimal}. * The scale of the {@code BigDecimal} is zero. * * @param val {@code BigInteger} value to be converted to * {@code BigDecimal}. */ public BigDecimal(BigInteger val) { scale = 0; intVal = val; intCompact = compactValFor(val); } /** * Translates a {@code BigInteger} into a {@code BigDecimal} * rounding according to the context settings. The scale of the * {@code BigDecimal} is zero. * * @param val {@code BigInteger} value to be converted to * {@code BigDecimal}. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * rounding mode is {@code UNNECESSARY}. * @since 1.5 */ public BigDecimal(BigInteger val, MathContext mc) { this(val,0,mc); } /** * Translates a {@code BigInteger} unscaled value and an * {@code int} scale into a {@code BigDecimal}. The value of * the {@code BigDecimal} is * (unscaledVal × 10-scale). * * @param unscaledVal unscaled value of the {@code BigDecimal}. * @param scale scale of the {@code BigDecimal}. */ public BigDecimal(BigInteger unscaledVal, int scale) { // Negative scales are now allowed this.intVal = unscaledVal; this.intCompact = compactValFor(unscaledVal); this.scale = scale; } /** * Translates a {@code BigInteger} unscaled value and an * {@code int} scale into a {@code BigDecimal}, with rounding * according to the context settings. The value of the * {@code BigDecimal} is (unscaledVal × * 10-scale), rounded according to the * {@code precision} and rounding mode settings. * * @param unscaledVal unscaled value of the {@code BigDecimal}. * @param scale scale of the {@code BigDecimal}. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * rounding mode is {@code UNNECESSARY}. * @since 1.5 */ public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) { // ... } /** * Translates an {@code int} into a {@code BigDecimal}. The * scale of the {@code BigDecimal} is zero. * * @param val {@code int} value to be converted to * {@code BigDecimal}. * @since 1.5 */ public BigDecimal(int val) { this.intCompact = val; this.scale = 0; this.intVal = null; } /** * Translates an {@code int} into a {@code BigDecimal}, with * rounding according to the context settings. The scale of the * {@code BigDecimal}, before any rounding, is zero. * * @param val {@code int} value to be converted to {@code BigDecimal}. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * rounding mode is {@code UNNECESSARY}. * @since 1.5 */ public BigDecimal(int val, MathContext mc) { //... } /** * Translates a {@code long} into a {@code BigDecimal}, with * rounding according to the context settings. The scale of the * {@code BigDecimal}, before any rounding, is zero. * * @param val {@code long} value to be converted to {@code BigDecimal}. * @param mc the context to use. * @throws ArithmeticException if the result is inexact but the * rounding mode is {@code UNNECESSARY}. * @since 1.5 */ public BigDecimal(long val, MathContext mc) { // ... } }


你可能感兴趣的:(JavaSE)