Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
[ 1.23e-23, 4.56, -1.7E1, 98.7e2, -0.27e-54 ].each{ printlnassert it } //decimals
assert (-1.23).class == BigDecimal
assert (-1.23g).class == BigDecimal
    //if we use the //BigInteger 'g' suffix after a decimal-formatted number, itmeans is a 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.

...

Code Block
assert (1234.567).unscaledValue() == 1234567g
    //returns the unscaled portion of a BigInteger

assert (1234.567).scale() == 3 //returns the scale

...

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

...

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

Formatted Displays

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

...

Code Block
[ 1.2345e7, 98.76e-3, 0.007, 0.000e4 ].each{
   assert new BigDecimal( it.toString() ) == it
}

...

Code Block
import java.text.*
def fmt= new DecimalFormat( '#,#00.0#' )
assert fmt.format( 5.6789d ) == '05.68'
    //pattern says at least 2 digits before point
assert fmt.format( 12345L ) == '12,345.0'
    //at least one digit after point, and comma as separator

...

Code Block
import java.text.*
assert new DecimalFormat( '#,##0.00;(#,##0.00)' ).format( -56L ) == '(56.00)'
       //pattern says parens around negative numbers
assert new DecimalFormat( '#,##0.00' ).format( -56L ) == '-56.00'
    //same as '#,##0.00;-#,##0.00'

...

Code Block
import java.text.*
assert new DecimalFormat( '0.###E0' ).format( 1234L ) == '1.234E3'

assert new DecimalFormat( '0.###E0;(0.###E0)' ).format( -0.001234 ) ==
    '(1.234E-3)'

assert new DecimalFormat( '00.###E0' ).format( 0.00123 ) == '12.3E-4'
[   12345: '12.345E3',  
  123456: '123.456E3'
].each{ assert new DecimalFormat( '##0.#####E0' ).format( it.key ) == it.value }
       // '##0' in pattern means exponent will be a multiple of 3

...

Code Block
import java.text.*
def dfs= new DecimalFormatSymbols()
    //use to give a different text for the exponent
assert dfs.zeroDigit == '0'
    //char used for zero. Different for Arabic, etc
assert dfs.groupingSeparator == ','
    //char used for thousands separator. Different for French, etc
assert dfs.decimalSeparator == '.' //char used for decimal point
assert dfs.digit == '#' //char used for a digit in a pattern
assert dfs.patternSeparator == ';'
    //char used to separate positive and negative subpatterns in a pattern
assert dfs.infinity == '∞' //char used to represent infinity
assert dfs.minusSign == '-'
    //char used for minus sign, used if no negative pattern specified
assert dfs.exponentSeparator == 'E'
    //string used to separate mantissa and exponent

dfs.exponentSeparator= 'x10^'
def fmt= new DecimalFormat( '0.####E0', dfs )
assert fmt.format( 123456 ) == '1.2346x10^5'
    //uses RoundingMode.HALF_EVEN by default

assert new DecimalFormat().format( Double.POSITIVE_INFINITY ) == '∞'

We'll look at DecimalFormats more in an upcoming tutorial on Internationalization.

Conversions

We can construct a BigDecimal from integers:

...

Code Block
def a= BigDecimal.valueOf( 12L, -3 );
assert a == 12000.0g; assert&& a.scale == -3

def b= BigDecimal.valueOf( 12L );
assert b == 12.0; assert&& 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

...

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

...

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

...

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

...

Code Block
assert 1/3 == 0.3333333333
    //BigDecimals with recurring decimaldecimals parts round their result to 10 decimal 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

...

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

...

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

...

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.

...

Code Block
import java.math.MathContext
import java.math.RoundingMode
    //so we don't have to qualify MathContextthese with java.math when we refer to itthem
import static java.math.RoundingMode.*
    //dittoso 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= [
         (RoundingMode.         (UP): [       6,       3,       2,       2,       1,     -1,     -2,     -2,     -3,     -6 ],
       (RoundingMode.      (DOWN): [       5,       2,       1,       1,       1,     -1,     -1,     -1,     -2,     -5 ],
       (RoundingMode.CEILING): [       6,       3,       2,       2,       1,     -1,     -1,     -1,     -2,     -5 ],
           (RoundingMode.FLOOR): [       5,       2,       1,       1,       1,     -1,     -2,     -2,     -3,     -6 ],
       (RoundingMode.HALF_UP): [       6,       3,       2,       1,       1,     -1,     -1,     -2,     -3,     -6 ],
   (RoundingMode.HALF_DOWN): [       5,       2,       2,       1,       1,     -1,     -1,     -2,     -2,     -5 ],
   (RoundingMode.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, RoundingMode.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 }
}

...

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

...

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.0 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

...

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.

...

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.0

//println( 1.1.pow(999999999) )
    //warning: this runs for a VERY LONG time when uncommented

...

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

...

Code Block
import java.math.RoundingMode
assert 25.497.divide( 123.4567, 5, RoundingMode.UP ) == 0.20653 20653
    //specify desired scale of 4, and rounding mode UP
assert 25.497.divide( 123.4567, 5, BigDecimal.ROUND_UP ) == 0.20653 20653
    //cater for pre-Java-5.0 syntax
assert 25.497.divide( 123.4567, RoundingMode.UP ) == 0.207 207
    //if no scale given, use same one as dividend (here, 25.497)
assert 25.497.divide( 123.4567, BigDecimal.ROUND_UP ) == 0.207

...

Code Block
import java.math.*
//import all classes prefixed with java.math, so we don't need to specify them individually
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 }
       //throw 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

...

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

...

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 )

...

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 }

...

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 13  //num.scale - 16

We can strip trailing zeros:

...