Want more information ?. Try the Web Home Page for this document.
This section compares the PSPDec class to the native Java BigDecimal on a number of features.
We conclude that PSPDec:
BigDecimal offers eight rounding options. PSPDec offers only one - the equivalent of BigDecimal's ROUND_HALF_DOWN. The single one we use is, in our opinion, the most consistent and easily understood. It also avoids the issue of potential pecuniary gain implicit in other rounding techniques.
However the option of rounding is offered far more frequently and consistently by PSPDec than by BigDecimal. For example, PSPDec offers rounding on all four arithmetic methods (add, subtract, multiply, divide) whereas BigDecimal offers it only for divide. This is because PSPDec takes a different approach to preserving numeric scale and assigning numeric values - discussed further below.
In conclusion, we are not convinced that business applications really need more than one arithmetic rounding method. We would offer more than one if our users demanded it, but it is not a high-priority item for us right now.
Click here for an illustration of rounding for PSPDec and other PSP decimal classes.
In your business applications you will frequently need to import/export numeric values as Strings to/from objects such as PSPDec and BigDecimal.
BigDecimal takes a very one-sided view of the decimal place to be used
in these operations. The BigDecimal documentation consistently refers to
"decimal point", ignoring the fact that for many countries the decimal
separator is a comma, not a full-stop. PSPDec in contrast gives you the setDecimalPlace(char)
and setDecimalPlaceDefault(char) methods to set your own decimal place character at object and class
levels. Furthermore you can specify the decimal place character explicitly on
PSPDec methods such as toString(char) and assign(String,char).
We conclude that PSPDec is far more aware of internationalisation issues than BigDecimal. Also beyond basic numeric string value formatting (sign, digits, decimal place only), PSPDec offers more complex formats via the PSPDecMask object. This masking facility is used consistently by all PSP decimal classes - PSPDec and others.
Neither BigDecimal nor PSPDec can offer true logical operators (==, !=,
etc.) because Java does not allow you to overload these. So PSPDec provides
equivalent methods isXX(PSPDec) where 'XX' can be EQ, NE, GT, GE, LT or LE. These
are missing from BigDecimal, which instead offers the min(BigDecimal) and max(BigDecimal)
methods. These return BigDecimal's not booleans, so are not quite the same
thing as isXX(PSPDec).
Both BigDecimal and PSPDec offer a signum() method. In addition, PSPDec
offers the isZero(), isPositive()
and isNegative() methods, which return booleans.
We conclude that PSPDec offers a wider range of logical methods, which should make your business applications easier to code and maintain.
For both PSPDec and BigDecimal objects, you need to be able to assign
the numeric value of one object to another. For example, assume Num1, Num2 and
Num3 are all PSPDec's or all BigDecimal's. If you want to assign the value of
Num2 to Num1, PSPDec offers the assign(..) and assignRound(...) methods. These methods
assign the value only - with user-defined rounding/truncation if the two
PSPDec's have a different number of digits either side of the decimal
place.
The BigDecimal class offers no such direct value assignment. You can of
course use the built-in Java assignment operator as in Num1 = Num2 but this is
assignment of the entire object, not just its value. After such an assignment,
Num1 has all properties identical to Num2 - not simply an identical numeric
value.
In general terms, Java offers no support for overloaded operators. So
whereas you can conveniently write Num1 = Num2 * Num3 if Num1, Num2 and Num3 are all
built-in Java data types, you can not easily do the same for objects. The best
you can do is ensure that your classes support methods equivalent to all
assignment and arithmetic operators.
We believe that in most business applications, multiple concatenated
arithmetic operations are quite rare. In most business code you do not often
find more than 3 or 4 sequential calculations involving common variables. Even
then developers typically want exact control over the size of any intermediate
decimal numbers in a string of calculations. In these circumstances we believe
that the PSPDec default assignment is more intuitive than that of BigDecimal.
PSPDec arithmetic operations assign to the LHS (the 'this' object), whereas
BigDecimal objects always return a value which you must then immediately assign
for a calculation to have any meaning. So for example Num1.multiply(Num2) is equivalent to
the '*=' operator for PSPDec, but equivalent to the '*' operator for
BigDecimal.
We conclude that PSPDec provides more convenient functionality than BigDecimal in the area of object value assignment. PSPDec may appear to be less intuitive than BigDecimal for concatenated operations but this is more than offset by the preservation of arithmetic accuracy - which is particularly important because the Java language provides no means of overloading both assignment and arithmetic operators.
The BigDecimal scale property and the PSPDec DigitsAfterDP property both represent the number of digits after the decimal place. PSPDec and BigDecimal have very different views of how/when the scale of a number should be increased after it is constructed.
PSPDec is simple - you only ever increase the scale using the setDigitsAfterDP(int)
method. If an operation such as multiply or divide generates an intermediate
result with more scale digits than the PSPDec itself, this is a
rounding/truncation issue. Therefore all PSPDec operations generating such an
intermediate result offer two alternatives e.g. multiply(..) and multiplyRound(..).
In contrast, BigDecimal always enlarges scale to the maximum required
for an intermediate result. For example, multiply(BigDecimal
that) always returns a new
BigDecimal whose scale is the sum of this.scale and that.scale. If you want to reset
scale after this type of operation, you do so using BigDecimal's setScale(int, int)
method.
We believe that you know the scale of your numbers in your business
applications. For example, you always want US$ amounts to have 2 decimal places
whereas you typically want to store percentages (e.g. discount rates) with 2
decimal places and currency exchange rates with 9 decimal places. If you apply
a discount to a US$ amount, do you always want back a US$ amount with 4 (2+2)
decimal places ?. If you convert US$ to Euro's, do you want back a Euro amount
with 11 (2+9) decimal places ?. We think not. In both cases you are highly
likely to know how you want the new value to be rounded/truncated, so want to
specify this during the numeric conversion, not afterwards using an operation
like setScale(int,
int).
In short, we believe PSPDec treats scale far more intuitively than BigDecimal. In the few cases where you do want operations to generate intermediate values with increased scale, you can simply use intermediate PSPDec's with the required scale.
Arithmetic operations can always increase the size of a decimal number on both sides of the decimal place. Increase in digits after the decimal place (scale) is covered above. This section covers increases in the number of integer (whole number) digits i.e. before the decimal place.
BigDecimal allows an infinite (probable limit is Integer.MAX_VALUE) number of
integer digits. This is always increased automatically.
PSPDec defaults to automatic increase of integer digits. However you can
change this using the setOverflow(byte) and setOverflowDefault(byte) methods. These methods allow
alternative treatments of 'overflow' i.e. a potential increase in the PSPDec
integer digits beyond the value determined by getDigitsBeforeDP(). This can be useful where
you are concerned that a decimal number size is sensible and you want to detect
breaches as early as possible. For example, an account balance may be limited
practically by the size of a field in a relational database. Or you may want a
percentage value never to exceed 99% i.e. should never have more than 2 integer
digits.
PSPDec integer size is subject to a practical limit determined by the
MAX_DIGITS_BEFORE constant. This currently has a value of 50. This limit is arbitrary and
we could increase it if our users asked us to do so. We believe this limit is
way beyond anything likely to be encountered in business applications.
In conclusion, PSPDec offers by default the same behaviour as BigDecimal for increases in integer digits, albeit with a lower theoretical ceiling. However PSPDec also offers alternative behaviour which you may find useful in real-world applications.
The table below summarises the time taken in milliseconds for 100,000 operations for PSPDec and BigDecimal objects. For all times, a percentage for PSPDec is calculated where the figure for BigDecimal is always base (100%).
|
Operation (100,000x) |
Time for PSPDec objects (ms) |
Time for BigDecimal objects (ms) |
| Construct from String then convert to String (5 digits) |
36360 (92%) |
39540 (100%) |
| Construct from String then convert to String (10 digits) |
46850 (104%) |
44930 (100%) |
| Construct from String then convert to String (20 digits) |
68650 (109%) |
62560 (100%) |
| Construct from String then convert to String (50 digits) |
130340 (113%) |
115780 (100%) |
| Add and Subtract two objects (5 digits) |
5060 (79%) |
6370 (100%) |
| Add and Subtract two objects (10 digits) |
5770 (91%) |
6320 (100%) |
| Add and Subtract two objects (20 digits) |
7090 (108%) |
6540 (100%) |
| Add and Subtract two objects (50 digits) |
10000 (139%) |
7190 (100%) |
| Multiply and divide two objects (5 digits) |
3020 (25%) |
12030 (100%) |
| Multiply and divide two objects (10 digits) |
14670 (106%) |
13790 (100%) |
| Multiply and divide two objects (20 digits) |
39000 (200%) |
19440 (100%) |
| Multiply and divide two objects (50 digits) |
121550 (579%) |
20980 (100%) |
In conclusion, PSPDec performance compared to BigDecimal performance is generally:
Each PSPDec and BigDecimal object allocates a certain amount of fixed storage (to store items such as the sign of the value) and a certain amount of variable storage - a byte array to hold the actual numeric value. For PSPDec the fixed storage amounts to 7 bytes, while for BigDecimal it amounts to 24 bytes. In contrast, the variable storage for BigDecimal (2's complement) is marginally more efficient than the most efficient packed decimal format for PSPDec.
The table below summarises the total storage requirements for numbers of varying sizes (total digits) assuming that the numeric value is initialised to all 9s.
| Total number of digits | Storage for PSPDec object (bytes) | Storage for BigDecimal object (bytes) |
|
1 |
8 (7 fixed + 1 variable) |
25 (24 fixed + 1 variable) |
|
5 |
10 (7 fixed + 3 variable) |
27 (24 fixed + 3 variable) |
|
10 |
13 (7 fixed + 6 variable) |
29 (24 fixed + 5 variable) |
|
15 |
15 (7 fixed + 8 variable) |
31 (24 fixed + 7 variable) |
|
20 |
18 (7 fixed + 11 variable) |
33 (24 fixed + 9 variable) |
|
25 |
20 (7 fixed + 13 variable) |
35 (24 fixed + 11 variable) |
|
30 |
23 (7 fixed + 16 variable) |
37 (24 fixed + 13 variable) |
|
50 |
33 (7 fixed + 26 variable) |
45 (24 fixed + 21 variable) |
|
75 |
45 (7 fixed + 38 variable) |
56 (24 fixed + 32 variable) |
|
100 |
58 (7 fixed + 51 variable) |
66 (24 fixed + 42 variable) |
In conclusion, PSPDec internal storage requirements are generally less than that of BigDecimal. The difference is more marked for smaller numeric values due to the higher fixed storage overhead of BigDecimal. It is only for very large numbers (more than 100 digits in the value) that BigDecimal is as efficient as PSPDec.