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.
| Code Block |
|---|
[ 1.23e-23, 4.56, -1.7E1, 98.7e2, -0.27e-54 ].each{ assert it } //decimals
assert (-1.23).class == BigDecimal
assert (-1.23g).class == BigDecimal
//BigInteger 'g' suffix after a decimal-formatted number means BigDecimal
|
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:
| Code Block |
|---|
assert new BigDecimal( 0, 1 ) == 0.0 assert new BigDecimal( 123, 0 ) == 123 assert new BigDecimal( 123 ) == 123 //default scale is 0 assert new BigDecimal( -123, 0 ) == -123 assert new BigDecimal( 123, -1 ) == 1.23e3 assert new BigDecimal( 12, -3 ) == 12000.0 assert new BigDecimal( 120, 1 ) == 12.0 assert new BigDecimal( 123, 5 ) == 0.00123 assert new BigDecimal( -123, 14 ) == -1.23e-12 assert (2 as BigDecimal).unscaledValue() == 2 assert (2 as BigDecimal).scale() == 0 assert (2 as BigDecimal).scale == 0 //parens optional assert 2.0.unscaledValue() == 20 assert 2.0.scale == 1 |
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:
| Code Block |
|---|
assert (1234.567).unscaledValue() == 1234567g
//returns the unscaled portion of a BigInteger
assert (1234.567).scale() == 3 //returns the scale
|
The precision of a BigDecimal is the number of digits in the unscaled value. The precision of a zero value is 1.
| Code Block |
|---|
assert 7.7.precision() == 2 assert (-7.7).precision() == 2 assert 1.000.precision() == 4 |
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.
| Code Block |
|---|
assert '23.45'.toBigDecimal() == 23.45
assert new BigDecimal( '23.45' ) == 23.45
assert new BigDecimal( '-32.8e2' ) == -32.8e2
assert new BigDecimal( '+.9E-7' ) == 0.9e-7
assert new BigDecimal( '+7.E+8' ) == 7e8
assert new BigDecimal( '0.0' ) == 0.0
try{ new BigDecimal( ' 23.45' ); assert 0 }
catch(e){ assert e instanceof NumberFormatException } //whitespace in string
|
If we have the String in a char array and are concerned with efficiency, we can supply that array directly to the BigDecimal:
| Code Block |
|---|
def ca1= ['1', '2', '.', '5'] as char[]
assert new BigDecimal( ca1 ) == 12.5
def ca2= [ 'a', 'b', '9', '3', '.', '4', '5', 'x', 'y', 'z' ] as char[]
assert new BigDecimal( ca2, 2, 5 ) == 93.45
//use 5 chars from the array beginning from index 2
|
There are some different ways of displaying a BigDecimal:
| Code Block |
|---|
assert 1.2345e7.toString() == '1.2345E+7'
//one digit before decimal point, if exponent used
assert 1.2345e7.toPlainString() == '12345000' //no exponent portion
assert 1.2345e7.toEngineeringString() == '12.345E+6' //exponent divisible by 3
|
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.
| Code Block |
|---|
[ 1.2345e7, 98.76e-3, 0.007, 0.000e4 ].each{
assert new BigDecimal( it.toString() ) == it
}
|
Conversions
We can construct a BigDecimal from integers:
| Code Block |
|---|
assert new BigDecimal( 45i ).scale == 0 assert new BigDecimal( 45L ).scale == 0 |
If we want to buffer frequently-used BigDecimal values for efficiency, we can use the valueOf() method:
| Code Block |
|---|
def a= BigDecimal.valueOf( 12L, -3 ) assert a == 12000.0g && a.scale == -3 def b= BigDecimal.valueOf( 12L ) assert b == 12.0 && b.scale == 0 //default scale is 0 assert BigDecimal.ZERO == 0.0 //These commonly-used values are pre-supplied assert BigDecimal.ONE == 1.0 assert BigDecimal.TEN == 10.0 |
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.
| Code Block |
|---|
assert 123g as BigDecimal == 123.0
assert 45i as BigDecimal == 45.0
assert 73L as BigDecimal == 73.0
assert 73L.toBigDecimal() == 73.0 //alternative syntax
assert 123.456 as BigInteger == 123g //lost information about the precision
assert 123.456.toBigInteger() == 123g //alternative syntax
assert 73.0 as Long == 73g
assert 73.0 as long == 73g
assert 73.0.toLong() == 73g
assert 73.0.longValue() == 73g //another alternative syntax
assert 45.6789.intValue() == 45g //truncated
assert 259.0.byteValue() == 3 //truncated, only lowest 8 integral bits returned
assert 200.789.byteValue() == -56
//truncated, only lowest 8 integral bits returned, with 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:
| Code Block |
|---|
assert 123.0.toBigIntegerExact() == 123g //lost information about the precision
try{ 123.456.toBigIntegerExact(); assert false }
catch(e){ assert e instanceof ArithmeticException }
assert 73.0.longValueExact() == 73g
[ { 73.21.longValueExact() },
{ 45.6789.intValueExact() },
{ 73.21.shortValueExact() },
{ 259.0.byteValueExact() },
{ 200.789.byteValueExact() },
].each{
try{ it(); assert false }catch(e){ assert e instanceof ArithmeticException }
}
|
BigDecimal Arithmetic
We can use the same methods and operators on BigDecimal we use with BigInteger:
| Code Block |
|---|
assert 3.4.plus( 3.3 ) == 3.4 + 3.3
assert 3.4.add( 3.3 ) == 3.4 + 3.3 //alternative name for plus
assert 3.4.minus( 2.1 ) == 3.4 - 2.1
assert 3.4.subtract( 2.1 ) == 3.4 - 2.1 //alternative name for minus
assert 3.0.multiply( 3.1 ) == 3.0 * 3.1
assert 3.0.multiply( 3g ) == 3.0 * 3g
assert 7.7.negate() == -7.7 //unary operation/method
assert (-7.7).negate() == -(-7.7)
assert (-7.7).plus() == +(-7.7) //this method provided for symmetry with negate
try{ 3.4.multiply(null); assert 0 }
catch(e){ assert e instanceof NullPointerException }
//all BigDecimal methods throw NullPointerException if passed a null
|
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:
| Code Block |
|---|
def a = 3.414, b = 3.3 assert a.scale() == 3 && b.scale() == 1 assert (a+b).scale() == 3 //max of 3 and 1 assert (a*b).scale() == 4 //sum of 3 and 1 |
For + - and *, a BigDecimal with any integer type converts it to a BigDecimal:
| Code Block |
|---|
assert (123.45g * 789).class == BigDecimal assert (123.45g * 789L).class == BigDecimal assert (123.45g * (89 as byte)).class == BigDecimal |
We can use a MathContext to change the precision of operations involving BigDecimals:
| Code Block |
|---|
def mc= new java.math.MathContext( 3 )
//precision of 3 in all constructors and methods where used
assert new BigDecimal( 123456, 0, mc ) == 123000g
assert new BigDecimal( -12345, 14, mc ) == -1.23e-10
assert new BigDecimal( '23.4567', mc ) == 23.5
assert new BigDecimal(
['2', '3', '.', '4', '5', '6', '7'] as char[], mc ) == 23.5
assert new BigDecimal(
['2', '3', '.', '4', '5', '6', '7'] as char[], 1, 5, mc ) == 3.46
assert new BigDecimal( 1234i, mc ) == 1230
assert new BigDecimal( 1234L, mc ) == 1230
assert 3.45678.add( 3.3, mc ) == 6.76
assert 0.0.add( 3.333333, mc ) == 3.33
assert 3.4567.subtract( 2.1, mc ) == 1.36
assert 0.0.subtract( 2.12345, mc ) == -2.12
assert 3.0.multiply( 3.1234, mc ) == 9.37
assert (-7.77777).negate( mc ) == 7.78
assert (-7.77777).plus( mc ) == -7.78
//effect identical to that of round(MathContext) method
|
Division
We can create BigDecimals by dividing integers, both fixed-size and BigInteger, for which the result is a decimal number:
| Code Block |
|---|
assert 7g / 4g == 1.75
assert (-7g) / 4g == -1.75
assert ( 1 / 2 ).class == BigDecimal
assert ( 1L / 2L ).class == BigDecimal
assert ( 1g / 2g ).class == BigDecimal
assert ( 1.5 * 2g ).class == BigDecimal
//an expression with a BigDecimal never converts to an integer
assert 1.0.div( 2 ).class == BigDecimal
//we can use a method instead of the operator
try{ 17g / 0; assert 0 }catch(e){ assert e instanceof ArithmeticException }
//division by 0 not allowed
|
Sometimes, the division can return a recurring number. This leads to a loss of exactness:
| Code Block |
|---|
assert 1/3 == 0.3333333333
//BigDecimals with recurring decimals round their result to 10 places...
assert ( (1/3) * 3 ) != 1
//...which leads to inaccuracy in calculations
assert (1/3).precision() == 10
assert 100000/3 == 33333.3333333333
//accuracy before the decimal point is always retained
|
When the scales of both operands in division are quite different, we can lose precision, sometimes even completely:
| Code Block |
|---|
assert (1.0 / 7.0) == 0.1428571429
//instead of "0.142857 with last 6 digits recurring"
assert (1e-5 / 7.0) == 0.0000014286 //precision is 10
assert (1e-9 / 7.0) == 0.0000000001
assert (1e-11 / 7.0) == 0.0
//difference in scale of operands can cause full loss of precision
|
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:
| Code Block |
|---|
assert 123.456.ulp() == 0.001 //always 1, but with same scale assert 123.456.ulp() == (-123.456).ulp() assert 0.00.ulp() == 0.01 |
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.
| Code Block |
|---|
assert 1.0.divide( 4.0 ) == 0.25
try{ 1.0.divide( 7.0 ); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
//result must be exact when using divide()
assert 1.234.divide( 4.0 ) == 0.3085
assert 1.05.divide( 1.25 )
assert 1.234.scale() == 3 && 4.0.scale() == 1 && 0.3085.scale() == 4
//scale of result unpredictable
assert 1.05.scale() == 2 && 1.25.scale() == 2 && 0.84.scale() == 2
|
We can change the precision of divide() by using a MathContext:
| Code Block |
|---|
assert (1.0).divide( 7.0, new java.math.MathContext(12) ) == 0.142857142857
//precision is 12
assert (1.0).divide( 7.0, new java.math.MathContext(10) ) == 0.1428571429
assert (1.0).divide( 7.0, new java.math.MathContext(5) ) == 0.14286
try{ 1.0.divide( 7.0, new java.math.MathContext(0) ); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
//precision of 0 same as if no MathContext was supplied
|
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:
| Code Block |
|---|
import java.math.MathContext
import java.math.RoundingMode
//so we don't have to qualify these with java.math when we refer to them
import static java.math.RoundingMode.*
//so we don't have to qualify UP, DOWN, etc with java.math.RoundingMode
def values= [ +5.5, +2.5, +1.6, +1.1, +1.0, -1.0, -1.1, -1.6, -2.5, -5.5 ]
def results= [
(UP): [ 6, 3, 2, 2, 1, -1, -2, -2, -3, -6 ],
(DOWN): [ 5, 2, 1, 1, 1, -1, -1, -1, -2, -5 ],
(CEILING): [ 6, 3, 2, 2, 1, -1, -1, -1, -2, -5 ],
(FLOOR): [ 5, 2, 1, 1, 1, -1, -2, -2, -3, -6 ],
(HALF_UP): [ 6, 3, 2, 1, 1, -1, -1, -2, -3, -6 ],
(HALF_DOWN): [ 5, 2, 2, 1, 1, -1, -1, -2, -2, -5 ],
(HALF_EVEN): [ 6, 2, 2, 1, 1, -1, -1, -2, -2, -6 ],
]
results.keySet().each{ roundMode->
def mc= new MathContext( 1, roundMode )
results[ roundMode ].eachWithIndex{ it, i->
assert new BigDecimal( values[i], mc ) == it
}
}
def mcu= new MathContext( 1, UNNECESSARY )
assert new BigDecimal( 1.0, mcu ) == 1
assert new BigDecimal( -1.0, mcu ) == -1
[ +5.5, +2.5, +1.6, +1.1, -1.1, -1.6, -2.5, -5.5 ].each{
try{ new BigDecimal( it, mcu ); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
}
|
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:
| Code Block |
|---|
import java.math.*
//imports all such classes, including both MathContext and RoundingMode
MathContext.UNLIMITED
//for unlimited precision arithmetic (precision=0 roundingMode=HALF_UP)
MathContext.DECIMAL32
//for "IEEE 754R" Decimal32 format (precision=7 roundingMode=HALF_EVEN)
MathContext.DECIMAL64
//Decimal64 format (precision=16 roundingMode=HALF_EVEN)
MathContext.DECIMAL128
//Decimal128 format (precision=34 roundingMode=HALF_EVEN)
assert MathContext.DECIMAL32.precision == 7
assert MathContext.DECIMAL32.roundingMode == RoundingMode.HALF_EVEN
//precision and roundingMode are properties
assert new BigDecimal( 123456789, 0, MathContext.DECIMAL32 ) == 123456800g
|
Other constructors for MathContext are:
| Code Block |
|---|
import java.math.*
def mc1= new MathContext( 3 )
//by default, uses RoundingMode.HALF_UP rounding mode
assert mc1.roundingMode == RoundingMode.HALF_UP
def mc2= new MathContext( 3, RoundingMode.HALF_UP )
assert mc2.toString() == 'precision=3 roundingMode=HALF_UP'
def mc3= new MathContext( mc2.toString() )
//we can initialize a MathContext from another's string
assert mc3.precision == 3
assert mc3.roundingMode == RoundingMode.HALF_UP
|
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:
| Code Block |
|---|
import java.math.*
def num= 2.2500
assert num.scale == 4 && num.unscaledValue() == 22500
def num2= num.setScale(5)
assert num2 == 2.25000 && num2.scale == 5 && num2.unscaledValue() == 225000
//usual use of changing scale is to increase the scale
def num3= num.setScale(3)
assert num3 == 2.25000 && num3.scale == 3 && num3.unscaledValue() == 2250
assert num.setScale(2) == 2.25
//only BigDecimal returned from method call has changed scale...
assert num.scale == 4 //...while original BigDecimal still has old scale...
num.scale= 3 //...so there's no point using the allowable property syntax
assert num.scale == 4
try{
num.setScale(1) //we can't change the value when we reduce the scale...
assert false
}catch(e){ assert e instanceof ArithmeticException }
assert 1.125.setScale(2, RoundingMode.HALF_UP) == 1.13
//...unless we use a rounding mode
assert 1.125.setScale(2, BigDecimal.ROUND_HALF_UP) == 1.13 //pre-Java-5 syntax
|
These 8 BigDecimal static fields are older pre-Java-5.0 equivalents for the values in the RoundingMode enum:
BigDecimal.ROUND_UP
BigDecimal.ROUND_DOWN
BigDecimal.ROUND_CEILING
BigDecimal.ROUND_FLOOR
BigDecimal.ROUND_HALF_UP
BigDecimal.ROUND_HALF_DOWN
BigDecimal.ROUND_HALF_EVEN
BigDecimal.ROUND_UNNECESSARY
There's two methods that let us convert such older names to the newer RoundingMode constants (enums):
| Code Block |
|---|
import java.math.RoundingMode
assert RoundingMode.valueOf( 'HALF_UP' ) == RoundingMode.HALF_UP
assert RoundingMode.valueOf( BigDecimal.ROUND_HALF_DOWN ) ==
RoundingMode.HALF_DOWN
|
Further operations
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.
| Code Block |
|---|
assert (4.5**3).class == Double assert 4.5.power(3).class == Double //using equivalent method instead |
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).
| Code Block |
|---|
assert 4.5.pow(3) == 91.125 //pow() is different to power()
assert (-4.5).pow(3) == -91.125
assert 4.5.pow(0) == 1.0
assert 0.0.pow(0) == 1.0
try{ 4.5.pow(-1); assert 0 }catch(e){ assert e instanceof ArithmeticException }
//exponent must be integer >=0
try{ 1.1.pow(1000000000); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
//exponent too high for Java 5
//println( 1.1.pow(999999999) )
//warning: this runs for a VERY LONG time when uncommented
|
When we supply a MathContext, the "ANSI X3.274-1996" algorithm is used:
| Code Block |
|---|
import java.math.MathContext
assert 4.5.pow( 3, new MathContext(4) ) == 91.13 //can supply a MathContext
assert 4.5.pow( -1, new MathContext(10) )
//negative exponents allowed when MathContext supplied
try{ 4.5.pow( -1, new MathContext(0) ); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
//ArithmeticException thrown if mc.precision == 0 and n < 0
try{ 4.5.pow( 123, new MathContext(2) ); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
//ArithmeticException thrown if mc.precision > 0 and
//n has more than mc.precision decimal digits
|
Instead of giving a precision via the MathContext, we can give the desired scale directly:
| Code Block |
|---|
import java.math.RoundingMode
assert 25.497.divide( 123.4567, 5, RoundingMode.UP ) == 0.20653
//specify desired scale of 4, and rounding mode UP
assert 25.497.divide( 123.4567, 5, BigDecimal.ROUND_UP ) == 0.20653
//cater for pre-Java-5.0 syntax
assert 25.497.divide( 123.4567, RoundingMode.UP ) == 0.207
//if no scale given, use same one as dividend (here, 25.497)
assert 25.497.divide( 123.4567, BigDecimal.ROUND_UP ) == 0.207
|
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.)
| Code Block |
|---|
import java.math.*
mc= new MathContext( 9, RoundingMode.HALF_UP )
assert 25.5.divide( 2.4, mc ) == 10.625
assert 25.5.divideToIntegralValue( 2.4 ) == 10 //rounding mode always DOWN...
assert 25.5.remainder( 2.4 ) == 1.5
assert 25.5.divideToIntegralValue( 2.4, mc ) == 10
//...even when a MathContext says otherwise
assert 25.5.remainder( 2.4, mc ) == 1.5
assert (-25.5).divideToIntegralValue( 2.4, mc ) == -10
assert (-25.5).remainder( 2.4, mc ) == -1.5
try{ 25.5.divideToIntegralValue( 0 ); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
try{ 25.5.remainder( 0 ); assert 0 }
catch(e){ assert e instanceof ArithmeticException }
assert 25.525.remainder( 2.345, new MathContext(1) ) == 2.075
//MathContext's precision only affects quotient calculation;
//remainder always exact so may have more decimal digits
[ [25.5, 2.4], [-27.1, 3.3] ].each{ x, y->
assert x.remainder( y ) ==
x.subtract( x.divideToIntegralValue( y ).multiply( y ) )
}
try{
2552.0.divideToIntegralValue( 2.4, new MathContext(2) )
assert 0
}catch(e){ assert e instanceof ArithmeticException }
//if result needs more decimal digits than supplied MathContext's precision
try{
2552.0.remainder( 2.4, new MathContext(2) )
assert 0
}catch(e){ assert e instanceof ArithmeticException }
//throw if implicit divideToIntegralValue() result needs more decimal digits
//than supplied MathContext's precision
def qr= 25.5.divideAndRemainder( 2.4 )
assert qr[0] == 10 && qr[1] == 1.5
//same results as divideToIntegralValue() and remainder(), but more efficient
|
We can find the absolute value of a BigDecimal:
| Code Block |
|---|
import java.math.* assert 7.89.abs() == 7.89 //same scale if no MathContext assert (-7.89).abs() == 7.89 assert (-7.89).abs( new MathContext(2) ) == 7.9 |
The round() operation only has a version with a MathContext parameter. Its action is identical to that of the plus(MathContext) method.
| Code Block |
|---|
assert 7.89.round( new MathContext(2) ) == 7.9 assert 7.89.round( new MathContext(0) ) == 7.89 //no rounding if precision is 0 |
Operations without a MathContext
Not all BigDecimal operations have a MathContext.
Auto-incrementing and -decrementing work on BigDecimals:
| Code Block |
|---|
def a= 12.315 a++ assert a == 13.315 --a assert a == 12.315 |
The signum method:
| Code Block |
|---|
assert 2.34.signum() == 1 assert (-2.34).signum() == -1 assert 0.0.signum() == 0 |
As with integers, we can compare BigDecimals:
| Code Block |
|---|
assert (2.50 <=> 2.5) == 0 && 2.50.compareTo(2.5) == 0 assert (-3.45 <=> 1.23) == -1 && (-3.45).compareTo(1.23) == -1 assert (1.23 <=> -0.12) == 1 && 1.23.compareTo(-0.12) == 1 assert (1.23 > -0.12) && 1.23.compareTo(-0.12) > 0 |
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().)
| Code Block |
|---|
assert ! ( 2.00.equals(2.0) )
//considers whether both unscaledValue and scale are equal
assert 2.00 == 2.0 //only considers the sequence of the two numbers on a line
assert 0.0 == -0.0 && 0.0.equals( -0.0 )
|
We can find the minimum and maximum of two BigDecimals:
| Code Block |
|---|
assert (-2.0).min( 7.3 ) == -2.0 assert 3.5.max( 4.2 ) == 4.2 |
We can move the decimal point to the left or right:
| Code Block |
|---|
import java.math.*
def num= 123.456
assert num.scale == 3
def mpl= num.movePointLeft( 2 )
assert mpl.scale == 5 //scale should be max( number.scale + movement, 0 )
assert mpl == 1.23456
def mpr= num.movePointRight( 4 )
assert mpr.scale == 0 //scale should be max( number.scale - movement, 0 )
assert mpr == 1234560
assert( 3.456.movePointLeft(2) == 0.03456 )
[ -2, -1, 0, 1, 2 ].each{
assert 123.456.movePointLeft( it ) == 123.456.movePointRight( -it )
}
try{ //throw ArithmeticException if scale will overflow on moving decimal point
new BigDecimal( 123456, 128*256*256*256 - 1 ).movePointLeft( 1 )
assert 0
}catch(e){ assert e instanceof ArithmeticException }
|
Another method for moving the decimal point, but by consistent change to the scale:
| Code Block |
|---|
import java.math.* def num= 123.456 assert num.scale == 3 def mpl= num.scaleByPowerOfTen( 16 ) assert mpl == 1.23456e18 assert mpl.scale == -13 //num.scale - 16 |
We can strip trailing zeros:
| Code Block |
|---|
assert 45.607000.stripTrailingZeros() == 45.607 assert 600.0.stripTrailingZeros() == 6e2 assert new BigDecimal( 6000, 1 ).stripTrailingZeros() == new BigDecimal( 6, -2 ) |