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