Decimal arithmetic
Copyright (c) IBM Corporation, 2000. All rights reserved. ©
| 11 Jan 2000 |
[contents | next]
|
The JavaTM runtime environment includes a class
(java.math.BigDecimal) for decimal arithmetic.
While suitable for simple financial calculations, it is missing a number
of features that are necessary for general-purpose decimal arithmetic.
This document describes what is missing, and proposes a small and upwards
compatible enhancement to the BigDecimal class which makes the necessary
additions.
Included in this document are:
- an overview of the proposal (this section)
- the concepts behind the design
- the description of the proposed classes
- a detailed definition of the arithmetic
- notes and change history.
The requirements
Java currently provides classes (java.math.BigDecimal and
java.math.BigInteger) for fixed point arithmetic, and also
supports native integer and binary floating point arithmetic directly.
Binary floating point is usually implemented by hardware, and therefore
is widely used for 'numerically intensive' work where performance
is critical.
However, with the growth in importance of applications where usability
is the primary concern, the anomalies of binary floating point
arithmetic (such as the inability to represent common values such as
0.10 exactly) are increasingly troublesome.[1]
In financial and commercial applications, especially, an arithmetic
which can achieve exact decimal results when required is essential.
This is the original purpose of the BigDecimal class.
Unfortunately, the current BigDecimal class provides only a limited set
of fixed point operations on numbers which are in practice limited to
those which can be represented conveniently as 'plain' numbers,
with no exponent. There are, in addition, a number of problems with
conversions to and from other Java types.
These, and the other problems with the BigDecimal class, are listed overleaf.
Problems with the BigDecimal class:
-
The fixed point (integer + scale) arithmetic is suitable for some tasks
(such as calculating taxes or balancing a check book), but is
inconvenient and awkward for many common applications.
For example, calculating the total amount repaid on a mortgage over 20
years is difficult, requiring several steps which do not involve exact
arithmetic and which may require explicit rounding.
For this task (and many others) an arithmetic that allows working to a
chosen precision is both simpler and more convenient.
-
Several operators commonly used in applications are missing,
specifically integer division, remainder, and exponentiation to an
integer power (as required for straightforward calculation of the
mortgage repayment just described, for example).
-
The constructors for BigDecimal do not accept exponential notation.
This means that results from other sources (for example, spreadsheets
and calculators, or the Java Double.toString() method) are
difficult to use.
-
The string form of a BigDecimal is always a plain number. This means
that very large or very small numbers are expressed using many
digits -- this makes them expensive and difficult to handle.
For many calculations an exponential or floating point representation is
desirable (and is potentially more efficient).
-
The conversions from BigDecimal to Java integer types are dangerous.
Specifically, they are treated as a narrowing primitive
conversion, even though there is a change of base involved. This
means that decimal parts of numbers can be dropped without warning, and
high order significant bits can also be lost without warning
(an error sometimes called 'decapitation'). It was exactly this
kind of error that caused the loss of the Ariane 5 launcher in 1996.[2]
In the proposal that follows, these deficiencies are addressed by
adding floating point arithmetic and exponential notation to the
BigDecimal class, in a fully upwards-compatible and seamless manner.
In addition, the set of base operators is completed, and new robust
conversion methods are added.
The proposal
This proposal answers the primary requirements of the last section by
adding support for decimal floating point arithmetic to the BigDecimal
class. This is achieved by simply adding a second parameter to the
existing operator methods.
The augmented class implements the decimal arithmetic defined in
the ANSI standard X3.274-1996,[3]
which has the following advantages:
-
The arithmetic was designed as a full-function decimal floating point
arithmetic, directly implementing the rules that people are taught at
school.
For example, number length information is not lost, so trailing zeros
can be correctly preserved in most operations: 1.20 times 2 gives 2.40,
not 2.4.
This behavior is essential both for usability and to maintain
compatibility with the existing BigDecimal class.
-
Being a true decimal arithmetic, exact results are given when expected
(for instance, 0.9/10 gives 0.09, not 0.089999996).
-
The precision of the arithmetic is freely selectable by the user, not
limited to a choice from one or two alternatives; where necessary,
calculations may be made using thousands of digits.
The operator definitions, and current implementations, impose no upper
limit on precision (though in practice, memory or processor constraints
will bound some calculations).
-
The arithmetic operations are robust; there is no 'wrap' of
integers at certain sizes, and ill-defined or out-of-range results
immediately throw exceptions.
-
The concept of a context for operations is explicit. This allows
application-global rules (such as precision and rounding) to be easily
implemented and modified. This aids testing and error analysis, as well
as simplifying programming.
-
Integers and fixed-scale numbers are a proper subset of all numbers.
Conversions to and from a different class are not necessary in order to
carry out integer and currency calculations.
-
A large range of numbers are supported; by definition, exponents in the
range of at least E-999999999 through E+999999999 are supported, with a
default precision of nine decimal digits.
Both scientific (where one digit is shown before the decimal point) and
engineering (where the power of ten is a multiple of three) exponential
notations are supported.
-
The arithmetic was developed over several years, based directly on user
feedback and requirements, and in consultation with professional
mathematicians and data processing experts. It has been heavily used
for over 16 years without problems, and was recently reviewed in depth
and ratified by the X3J18 committee for ANSI.
-
Numerous public-domain and commercial implementations of the arithmetic
exist.
IBM has implementations in C, C++, various Assembler languages, and for
Java.
This arithmetic has been further enhanced by supporting a variety of
rounding algorithms, as already defined in Java 1.1 for
the java.math.BigDecimal class.
A prototype of the proposed enhanced BigDecimal class has been specified
(see the remainder of this document) and has been fully implemented,
including javadoc comments following Java guidelines and an appropriate
set of test cases.
It is a small class (at approximately 23,000 bytes, including line number
tables, it is smaller than the BigInteger class in Java 1.2), and does
not use any native methods.
The class is based on code that has been in use since 1996, and
which has been packaged as a BigDecimal class since June 1998.
It has been available on the IBM alphaWorks site since late 1998.
For reasons explained later, the detail in this document also proposes
adding one very small new context class to Java, in addition to
enhancing the BigDecimal class.
The new class would most logically be added to the java.math
package in the Java Runtime Environment, and it is suggested that it be
called MathContext.
The changes to the Java runtime proposed are summarized on the next
page.
The changes to the current Java API affect only two classes; BigDecimal
(which is enhanced from the current specification) and MathContext
(which is new).
- BigDecimal
-
Instantiates a decimal number, and includes:
-
Constructors and methods for creating a BigDecimal number from the
primitive Java types, and from strings and BigInteger object.
Four constructors and one method have been added.
-
Operator methods, for the usual arithmetic operators, including
comparisons. Four new operators have been added, and all operator
methods have a second version which specifies a context.
-
Other methods, including standard Java methods (equals, hashCode,
etc.), and conversions to primitive types and String
(intValueExact, floatValue, toString, format, signum, etc.).
Seven methods have been added, mostly to effect robust (error-detecting)
conversions.
This initial proposal does not include transcendental functions.
- MathContext
-
A very small class, used for defining a context for arithmetic,
as described in the next section. This comprises four constructors and
five methods (four 'get' methods and a toString() method).
These classes are available for testing in the package com.ibm.math
(that is, as the classes com.ibm.math.BigDecimal
and com.ibm.math.MathContext).
Comments on them and on this draft are welcome. Please send
comments to Mike Cowlishaw, mfc@speleotrove.com.
Acknowledgements
Very many people have contributed to the arithmetic described in this
document, especially the IBM REXX language committee, the IBM Vienna
Compiler group, and the X3 (now NCITS) J18 technical committee.
Special thanks for their contributions to the current design are due
to Joshua Bloch, Dirk Bosmans, and Brian Marks.
Footnotes:
[1] |
See, for example, Floating point issues,
C. Sweeney, at:
http://www.truebasic.com/tech08.html
|
[2] |
See: http://www.esrin.esa.it/htdocs/tidc/Press/Press96/ariane5rep.html
|
[3] |
American National Standard for Information Technology --
Programming Language REXX, X3.274-1996, American National
Standards Institute, New York, 1996.
|
[contents | next]