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.
[ 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:
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:
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.
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.
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:
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:
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.
[ 1.2345e7, 98.76e-3, 0.007, 0.000e4 ].each{
assert new BigDecimal( it.toString() ) == it
}
Conversions
We can construct a BigDecimal from integers:
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:
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.
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:
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:
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:
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:
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:
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:
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:
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:
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:
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.
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:
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:
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:
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:
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:
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):
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.
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).
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:
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:
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.)
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:
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.
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:
def a= 12.315 a++ assert a == 13.315 --a assert a == 12.315
The signum method:
assert 2.34.signum() == 1 assert (-2.34).signum() == -1 assert 0.0.signum() == 0
As with integers, we can compare BigDecimals:
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().)
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:
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:
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:
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:
assert 45.607000.stripTrailingZeros() == 45.607 assert 600.0.stripTrailingZeros() == 6e2 assert new BigDecimal( 6000, 1 ).stripTrailingZeros() == new BigDecimal( 6, -2 )






