Decimal arithmetic
Copyright (c) IBM Corporation, 2000. All rights reserved. © | 11 Jan 2000 |
[previous | contents | next] |
It is important to note that this definition describes results (objects of type BigDecimal) only in terms of their external representation -- that is, when viewed with the toString method. This guarantees that there is no hidden information in the internal representation of the numbers ('what you see is exactly what you've got') and that the intermediate results of any calculation are always defined and inspectable.
Within this constraint, any internal representation of a BigDecimal may be used, provided that the results are identical to those that would result from converting any BigDecimal object to type String and then back to a BigDecimal object at any point in this definition.
'12'.add('7.00',def) ==> '19.00'means 'new BigDecimal("12").add(new BigDecimal("7.00")) returns a BigDecimal object exactly equal to new BigDecimal("19.00")', or, more formally, it means that the term:
new BigDecimal("12").add(new BigDecimal("7.00")).toString().equals( new BigDecimal("19.00").toString())evaluates to true.
Finally, in this example and in thee examples below, the name def is assumed to be a reference to the MathContext.DEFAULT object.
Formally:
sign ::= + | - digits ::= digit [digit]... numeric ::= digits . [digits] | [.] digits number ::= [sign] numericwhere a digit is any decimal digit character, such that the value returned by
java.lang.Character.digit(c, 10)(where c is the character in question) is not -1 and defines the value of the digit (0 through 9). Any decimal digit character outside the range '0' through '9' is treated as though it were the corresponding character within that range.
Note that a single period alone is not a valid number.
Numbers may also include an exponent, as explained later.
If the digits setting is greater than 0, it defines the precision (number of significant digits) to which arithmetic calculations will be carried out; results will be rounded to that precision, if necessary. If the digits setting is 0, then the precision is not limited to a specific number of digits; the precision used is implied by the operands depending on the specific operation, as described below.
If MathContext.DEFAULT is passed as a MathContext parameter, then the context is said to be default, and a precision of nine digits is used.
If no MathContext object is provided for an operation,[2] then the digits setting used is 0 (that is, unlimited precision).
An implementation-dependent maximum for the digits setting (equal to or larger than 1000) may apply: an attempt to create or change a MathContext object so that its digits setting exceeds the maximum will cause an ArithmeticException to be thrown. Thus if an algorithm is defined to use more than 1000 digits then if the MathContext object can be set up without an exception then the computation will proceed and produce identical results to any other implementation.[3]
Note that digits may be set to positive values below the default of nine. Small values, however, should be used with care -- the loss of precision and rounding thus requested will affect all computations that use the low-precision MathContext object, including comparisons.
In the remainder of this section, the notation digits refers to the digits setting in use, either from a MathContext object or by omission.
If digits is not 0 for an arithmetic operation, the operand or operands (the numbers being operated upon) have leading zeros removed (noting the position of any decimal point, and leaving just one zero if all the digits in the number are zeros) and are then rounded to digits significant digits[4] (if necessary) before being used in the computation. The operation is then carried out under up to double that precision, as described under the individual operations below. When the operation is completed, the result is rounded (again, if necessary) to digits digits.
If digits is 0 for an arithmetic operation, then the precision used for the calculation is not limited by length, and in this case rounding is unnecessary, except for the results of the division and power operators.
By default, all rounding is done in the 'classical' manner, in that the extra (guard) digit is inspected and values of 5 through 9 are rounded up, and values of 0 through 4 are rounded down. Alternative rounding algorithms (such as even/odd rounding) may be selected by changing the round setting of the MathContext object used for an operation.[5]
In results, a decimal point is only included if it will be followed by one or more decimal digits. A conventional zero is supplied preceding a decimal point if otherwise there would be no digit before it. If the form setting is either ENGINEERING or SCIENTIFIC, then (as required by convention) a result of zero is expressed as the single character '0'. Otherwise, trailing zeros are retained for all operations, except as described below (for example, for division when the form setting is not PLAIN).
The format method is defined to allow a number to be represented in a particular format if the standard result provided by the toString method does not meet requirements.
(The BigDecimal add, subtract, plus, and negate methods.)
If either number is zero and the form setting is not PLAIN then the other number, rounded to digits digits if necessary, is used as the result (with sign and form adjustment as appropriate).[6] Otherwise, the two numbers are aligned at their units digit and are then extended on the right and left as necessary up to a total maximum of digits+1 digits[7] (or as far as is needed to use all the digits of both numbers, if digits is 0). The numbers are then added or subtracted as appropriate.
For example, the addition
'xxxx.xxx'.add('yy.yyyyy', context)(where 'x' and 'y' are any decimal digits) becomes:
xxxx.xxx00 + 00yy.yyyyy ------------ zzzz.zzzzzIf digits is not 0 the result is then rounded to digits digits if necessary, taking into account any extra (carry) digit on the left after an addition, but otherwise counting from the position corresponding to the most significant digit of the terms being added or subtracted. Finally, any insignificant leading zeros are removed.
The monadic (plus and negate) methods are evaluated using the same rules; the terms a.plus() and b.negate() (where a and b refer to any BigDecimal objects) are calculated as BigDecimal.ZERO.add(a) and BigDecimal.ZERO.subtract(b) respectively (using the original MathContext parameter, if any).
(The BigDecimal multiply method.)
The numbers are multiplied together ('long multiplication') resulting in a number which may be as long as the sum of the lengths of the two operands. For example:
'xxx.xxx'.multiply('yy.yyyyy', context)becomes:
'zzzzz.zzzzzzzz'If digits is not 0 the result is then rounded to digits digits if necessary, counting from the first significant digit of the result.
(The BigDecimal divide methods.)
For the division:
'yyy'.divide('xxxxx', context)the following steps are taken: first, the number 'yyy' is extended with zeros on the right until it is larger than the number 'xxxxx' (with note being taken of the change in the power of ten that this implies). Thus in this example, 'yyy' might become 'yyy00'. Traditional long division then takes place, which can be written:
zzzz .------ xxxxx | yyy00The length of the result ('zzzz') is such that the rightmost 'z' will be at least as far right as the rightmost digit of the (extended) 'y' number in the example. During the division, the 'y' number will be extended further as necessary, and the 'z' number (which will not include any leading zeros) is also extended as necessary until the division is complete.
If digits is not 0, the 'z' number may increase up to digits+1 digits, at which point it is rounded to digits digits (taking into account any residue) to form the result.
If digits is 0, the 'z' number is increased until its scale (digits after the decimal point) is the same as the scale of the original 'y' number (or the explicitly specified scale for the divide method that takes a scale parameter). An additional digit is then computed (as though the scale were larger by one); this digit is then used, if required, to round the 'z' number (taking into account any residue) to form the result.
Finally, if the form setting is either ENGINEERING or SCIENTIFIC, any insignificant trailing zeros are removed.
Examples of the base operators:
'12'.add('7.00') ==> '19.00' '1.3'.subtract('1.07') ==> '0.23' '1.3'.subtract('2.07') ==> '-0.77' '1.20'.multiply('3') ==> '3.60' '7'.multiply('3') ==> '21' '0.9'.multiply('0.8') ==> '0.72' '1'.divide('3',def) ==> '0.333333333' '2'.divide('3',def) ==> '0.666666667' '5'.divide('2',def) ==> '2.5' '1'.divide('10',def) ==> '0.1' '12'.divide('12',def) ==> '1' '8.0'.divide('2',def) ==> '4'
Note: Except for division under unlimited precision, the position of the decimal point in the terms being operated upon by the base operators is arbitrary. The operations may be carried out as integer operations with the exponent being calculated and applied afterwards. Therefore the significant digits of a result are not in any way dependent on the position of the decimal point in either of the terms involved in the operation.
(The BigDecimal pow method.)
The power operator raises a number (the left-hand operand) to a whole number power (the right-hand operand).
If digits is not 0, the right-hand operand must be a whole number whose integer part has no more digits than digits and whose decimal part (if any) is all zeros. It may be positive, negative, or zero; if negative, the absolute value of the power is used, and then the result is inverted (divided into 1).
If digits is 0, the right-hand operand must be a non-negative whole number whose decimal part (if any) is all zeros.
For calculating the power, the number is effectively multiplied by itself for the number of times expressed by the power, and finally trailing zeros are removed (as though the result were divided by one).
In practice (see note below for the reasons), the power is calculated by the process of left-to-right binary reduction. For 'x.pow(n)': 'n' is converted to binary, and a temporary accumulator is set to 1. If 'n' has the value 0 then the initial calculation is complete. Otherwise each bit (starting at the first non-zero bit) is inspected from left to right. If the current bit is 1 then the accumulator is multiplied by 'x'. If all bits have now been inspected then the initial calculation is complete, otherwise the accumulator is squared by multiplication and the next bit is inspected. When the initial calculation is complete, the temporary result is divided into 1 if the power was negative.
The multiplications and division are done under the normal arithmetic operation and rounding rules, using the context supplied for the operation, except that if digits is not 0, the multiplications (and the division, if needed) are carried out using a precision of digits+elength+1 digits. Here, elength is the length in decimal digits of the integer part of the whole number 'n' (i.e., excluding any sign, decimal part, decimal point, or insignificant leading zeros, as though the operation n.abs().divideInteger(BigDecimal.ONE,context) had been carried out using digits digits). The result is then rounded to digits digits.
If digits is 0, then the context is unchanged; the multiplications are carried out with with digits set to 0 (that is, unlimited precision).
Finally, if the form setting is either ENGINEERING or SCIENTIFIC, any insignificant trailing zeros are removed.
(The BigDecimal divideInteger method.)
The integer divide operator divides two numbers and returns the integer part of the result. The result returned is defined to be that which would result from repeatedly subtracting the divisor from the dividend while the dividend is larger than the divisor. During this subtraction, the absolute values of both the dividend and the divisor are used: the sign of the final result is the same as that which would result if normal division were used.
The result returned will have no decimal part (that is, no decimal point or zeros following it). If digits is not 0 and the result cannot be expressed exactly within digits digits, the operation is in error and will fail -- that is, the result cannot have more digits than the value of digits in effect for the operation. For example, '10000000000'.divideInteger('3',context) requires ten digits to express the result exactly ('3333333333') and would therefore fail if digits were in the range 1 through 9.
(The BigDecimal remainder method.)
The remainder operator will return the remainder from integer division, and is defined as being the residue of the dividend after the operation of calculating integer division as just described, then rounded to digits digits (if digits is not 0). The sign of the result, if non-zero, is the same as that of the original dividend.
This operation will fail under the same conditions as integer division (that is, if integer division on the same two terms would fail, the remainder cannot be calculated).
Examples of the additional operators:
'2'.pow('3',def) ==> '8' '2'.pow('-3',def) ==> '0.125' '1.7'.pow('8',def) ==> '69.7575744' '2'.divideInteger('3',def) ==> '0' '2.1'.remainder('3',def) ==> '2.1' '10'.divideInteger('3',def) ==> '3' '10'.remainder('3',def) ==> '1' '-10'.remainder('3',def) ==> '-1' '10.2'.remainder('1',def) ==> '0.2' '10'.remainder('0.3',def) ==> '0.1' '3.6'.remainder('1.3',def) ==> '1.0'Notes:
Numeric comparison, using this method, is effected by subtracting the two numbers (calculating the difference, as though by using the subtract method with the same parameter or parameters) and then returning the sign of the result (-1 if the result is negative, 0 if the result is zero, or 1 if the result is positive). For example, the operation:
a.compareTo(b,c)returns the same result as:
a.subtract(b,c).signum()It is therefore the difference between two numbers, when subtracted under the rules for decimal subtraction, that determines their equality.
When the signs of the operands are different the signs of the operands (-1, 0, or 1) are used for the comparison instead of the operands themselves.[8]
Examples:
'10000000000'.multiply('10000000000') ==> '100000000000000000000' '0.00000000001'.multiply('0.00000000001') ==> '0.0000000000000000000001'For both large and small numbers, however, some form of exponential notation is useful, both to make such long numbers more readable and to make evaluation possible in extreme cases. In addition, exponential notation is normally used whenever the plain form would give misleading information. For example, if digits were set to 5 in the MathContext object referred to as context then:
'54321'.multiply('54321',context)would round the multiplication to five digits and hence give the result '2950800000' if plain form were to be used. This is misleading, as it appears that the result is an exact multiple of 100000; instead, the result will by default be expressed in exponential notation, in this case as '2.9508E+9'.
The definition of number (see above) is therefore extended by replacing the description of numeric by the following:
significand ::= digits . [digits] | [.] digits numeric ::= significand [E [sign] digits]In other words, the numeric part of a number may be followed by an 'E' (indicating an exponential part), an optional sign, and an integer following the sign that represents a power of ten that is to be applied. The 'E' may be in uppercase or lowercase. Note that no blanks are permitted within this part of a number, but the integer may have leading zeros.
Examples:
The following all result in 0:
'12E+11'.compareTo('1200000000000',def) '12E-5'.compareTo('0.00012',def) '12e4'.compareTo('120000',def)All valid numbers, expressed as a String, may be used to construct a BigDecimal object.
The results of calculations will be expressed in exponential form depending on their value and the value of digits at the time of the calculation: if the number of places needed before the decimal point exceeds digits (and digits is not 0), or if the absolute value of the result is less than '0.000001' (regardless of the value of digits), then exponential form will be used.
The exponential form generated by the toString method always has a sign following the 'E' for readability, and the 'E' is in uppercase. If the exponent is 0 then the exponential part is omitted -- that is, an exponential part of 'E+0' will never be generated.
If the default format for a number is not satisfactory for a particular application, then the format method may be used to control its appearance.
Different exponential notations for the result of an operation may be selected with the form setting in a MathContext object. This setting allows the selection of either scientific or engineering notation. Scientific notation (the default) adjusts the power of ten so there is a single non-zero digit to the left of the decimal point. Engineering notation causes powers of ten to be expressed as a multiple of three -- the integer part may therefore range from 1 through 999.
For example, with scientific notation:
'123.45'.multiply('1e11', def) ==> '1.2345E+13'and with engineering notation (where eng is a MathContext object with form set to MathContext.ENGINEERING):
'123.45'.multiply('1e11', eng) ==> '12.345E+12'In addition, plain notation may be requested. This forces the result to be represented as a plain number, without using exponential notation, regardless of the value of digits and the value of the result.
When enabled by the lostDigits setting of the provided MathContext object being true, then if digits is not 0 then the operator methods first check that the number of significant digits in all their BigDecimal operands is less than or equal to the digits setting of the provided MathContext object; if this condition is not met, then an exception is thrown.
Note that trailing zeros are in this case are not significant; if digits had the value 5, then none of
0.12345 123.45 12345 12345.0000 1234500000would throw an exception (whereas 12345.1 or 1234500001 would).
In addition, other runtime exceptions may be thrown when invalid parameters are passed to a method in the BigDecimal or MathContext classes.
[1] | American National Standard for Information Technology -- Programming Language REXX, X3.274-1996, American National Standards Institute, New York, 1996. |
[2] | That is, when the form of the method that takes no MathContext is used. |
[3] | The com.ibm.math.BigDecimal implementation for Java puts a limit of 999999999 on the digits setting. |
[4] | If rounding of operands is undesirable, then the lostDigits setting of the provided MathContext object can be used to cause an exception to be thrown if rounding would be applied. |
[5] | Or by using the explicit rounding mode parameter on the two divide methods from the original BigDecimal class. |
[6] | If the form setting is PLAIN, this short cut cannot be taken as trailing zeros may appear in a zero result. |
[7] | The number with the smaller absolute value may therefore lose some or all of its digits on the right. In the example, the number 'yy.yyyyy' would have three digits truncated if digits were 5. |
[8] | This rule removes the possibility of an arithmetic overflow during a numeric comparison. |