We can only use base-10 notation to represent decimal numbers, not hexadecimal or octal. Decimals are written with a decimal part and/or an exponent part, each with an optional + -. The leading zero is required.
Such BigDecimals are arbitrary-precision signed decimal numbers. They consist of an unscaled infinitely-extendable value and a 32-bit Integer scale. The value of the number represented by it is (unscaledValue × 10**(-scale)). This means a zero or positive scale is the number of digits to the right of the decimal point; a negative scale is the unscaled value multiplied by ten to the power of the negation of the scale. For example, a scale of -3 means the unscaled value is multiplied by 1000.
We can construct a BigDecimal with a specified scale:
All methods and constructors for this class throw NullPointerException when passed a null object reference for any input parameter.
We can enquire the scale of a BigDecimal:
The precision of a BigDecimal is the number of digits in the unscaled value. The precision of a zero value is 1.
We can construct a BigDecimal from a string. The value of the resulting scale must lie between Integer.MIN_VALUE and Integer.MAX_VALUE, inclusive.
If we have the String in a char array and are concerned with efficiency, we can supply that array directly to the BigDecimal:
There are some different ways of displaying a BigDecimal:
From Java 5.0, every distinguishable BigDecimal value has a unique string representation as a result of using toString(). If that string representation is converted back to a BigDecimal, then the original value (unscaled-scale pair) will be recovered. This means it can be used as a string representation for exchanging decimal data, or as a key in a HashMap.
We can format integers and decimals using patterns with DecimalFormat:
We can explicitly define a text for negative numbers, separating the positive and negative formats with a semicolon. If the negative text is omitted, numbers are prefixed with the negative sign.
We can format decimal numbers:
We can change various settings using DecimalFormatSymbols:
We'll look at DecimalFormats more in an upcoming tutorial on Internationalization.
We can construct a BigDecimal from integers:
If we want to buffer frequently-used BigDecimal values for efficiency, we can use the valueOf() method:
The BigDecimal can be converted between the BigInteger, Integer, Long, Short, and Byte classes. Numbers converted to fixed-size integers may be truncated, or have the opposite sign.
By appending 'Exact' to the asLong()-style method names, we can ensure an ArithmeticException is thrown if any information would be lost in the conversion:
We can use the same methods and operators on BigDecimal we use with BigInteger:
The scale resulting from add or subtract is the maximum scale of each operand; that resulting from multiply is the sum of the scales of the operands:
For + - and *, a BigDecimal with any integer type converts it to a BigDecimal:
We can use a MathContext to change the precision of operations involving BigDecimals:
We can create BigDecimals by dividing integers, both fixed-size and BigInteger, for which the result is a decimal number:
Sometimes, the division can return a recurring number. This leads to a loss of exactness:
When the scales of both operands in division are quite different, we can lose precision, sometimes even completely:
The ulp() of a BigDecimal returns the "Units of the Last Place", the difference between the value and next larger having the same number of digits:
Another way of dividing numbers is to use the divide() method, different to the div() method and / operator. The result must be exact when using divide(), or an ArithmeticException is thrown.
We can change the precision of divide() by using a MathContext:
MathContext Rounding Modes
As well as specifying required precision for operations in a MathContext, we can also specify the rounding behavior for operations discarding excess precision. Each rounding mode indicates how the least significant returned digit of a rounded result is to be calculated.
If fewer digits are returned than the digits needed to represent the exact numerical result, the discarded digits are called "the discarded fraction", regardless their contribution to the value of the number returned. When rounding increases the magnitude of the returned result, it is possible for a new digit position to be created by a carry propagating to a leading 9-digit. For example, the value 999.9 rounding up with three digits precision would become 1000.
We can see the behaviour of rounding operations for all rounding modes:
We can thus see:
UP rounds away from zero, always incrementing the digit prior to a non-zero discarded fraction.
DOWN rounds towards zero, always truncating.
CEILING rounds towards positive infinity (positive results behave as for UP; negative results, as for DOWN).
FLOOR rounds towards negative infinity (positive results behave as for DOWN; negative results, as for UP).
HALF_UP rounds towards nearest neighbor; if both neighbors are equidistant, rounds as for UP. (The rounding mode commonly taught in US schools.)
HALF_DOWN rounds towards nearest neighbor; if both neighbors are equidistant, rounds as for DOWN.
HALF_EVEN rounds towards the nearest neighbor; if both neighbors are equidistant, rounds towards the even neighbor. (Known as "banker's rounding.")
UNNECESSARY asserts that the operation has an exact result; if there's an inexact result, throws an ArithmeticException.
There are some default rounding modes supplied for use:
Other constructors for MathContext are:
The rounding mode setting of a MathContext object with a precision setting of 0 is not used and thus irrelevant.
Cloning BigDecimals but with different scale
We can create a new BigDecimal with the same overall value as but a different scale to an existing one:
These 8 BigDecimal static fields are older pre-Java-5.0 equivalents for the values in the RoundingMode enum:
There's two methods that let us convert such older names to the newer RoundingMode constants (enums):
For the other arithmetic operations, we also usually have the choice of supplying a MathContext or not.
There's two main ways to raise a number to a power. Using ** and power() returns a fixed-size floating-point number, which we'll look at in the next topic on Groovy Floating-Point Math.
We can raise a BigDecimal to the power using the pow() method instead, which always returns an exact BigDecimal. However, this method will be very slow for high exponents. The result can sometimes differ from the rounded result by more than one ulp (unit in the last place).
When we supply a MathContext, the "ANSI X3.274-1996" algorithm is used:
Instead of giving a precision via the MathContext, we can give the desired scale directly:
We can divide to an integral quotient, and/or find the remainder. (The preferred scale of the integral quotient is the dividend's less the divisor's.)
We can find the absolute value of a BigDecimal:
The round() operation only has a version with a MathContext parameter. Its action is identical to that of the plus(MathContext) method.
Operations without a MathContext
Not all BigDecimal operations have a MathContext.
Auto-incrementing and -decrementing work on BigDecimals:
The signum method:
As with integers, we can compare BigDecimals:
The equals() method and == operator are different for BigDecimals. (So we must be careful if we use BigDecimal objects as elements in a SortedSet or keys in a SortedMap, since BigDecimal's natural ordering is inconsistent with equals().)
We can find the minimum and maximum of two BigDecimals:
We can move the decimal point to the left or right:
Another method for moving the decimal point, but by consistent change to the scale:
We can strip trailing zeros: