home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 2
/
ctrom_ii_b.zip
/
ctrom_ii_b
/
PROGRAM
/
C
/
MONEY
/
MONEY.VDE
< prev
Wrap
Text File
|
1993-08-09
|
14KB
|
332 lines
Yet Another C++ Money Class
por
Adolfo Di Mare
BITNET: adimare@UCRVM2
(506) 53-4853
Reporte Técnico PIBDC-02-91
Revisión 1.0
Proyecto 326-89-019
Resumen: Se presenta una eficiente y simple clase C++ para
manipular datos que representan dinero. La clase se implementa
usando números de punto flotante dobles.
Abstract: An efficient and simple C++ class to handle money
quantities is presented. The class uses scaled doubles to
represent money data variables.
Adolfo Di Mare is Associate professor at the Universidad de Costa
Rica, where he teaches programming. His research interest are the
use of Turbo Pascal and C++ to solve information system problems.
Esta investigación se realizó dentro del proyecto de investigación
326-89-019 "Estudios de la Tecnología de Programación por Objetos
y C++", inscrito ante la Vicerrectoría de Investigación de la
Universidad de Costa Rica. La Escuela de Ciencias de la
Computación e Informática también ha aportado fondos para realizar
este trabajo.
.FO money.h # Adolfo Di Mare
Yet Another C++ Money Class
===========================
I use C to program information systems where handling money
quantities is quite common. Using doubles to represent money
quantities is not good enough, because decimals are not
represented correctly and sometimes round-off errors occur. A
while ago a friend of mine gave me a very good idea to overcome
this problem: he works using Canon BASIC, and he has always used
floor(double*100.0) to represent money quantities. Any amount is
represented as a double with no fractional part. With this trick
no decimals are lost because the double type, with its 15+ digit
precision, is used as a compiler supported long long. You need to
be careful when multiplying and dividing, but in many applications
these operations are rare.
For example, $25.35 is represented as the double 2535.0, using a
scale factor of 100 (for two decimal places). The problem I kept
facing in my C programs was to remember when to multiply by 100
and when not to. For example, to add two doubles that represent
money quantities there is no need to multiply by 100:
$25.35 + $35.75 <==> 2535.0 + 3575.0 == 6110.0 <==> $61.10
Also, one should never multiply by the scale factor when
multiplying a money quantity:
$100 * (1.0+0.06) <==> 10000.0 * (1.06) == 10600.0 <==> $106
It is just too easy to forget to multiply by the scale factor when
using doubles as money. We human beings forget too much, so we
need the computer to remember for us.
When I started programming in C++, I realized that a C++ money
class was the solution for these problems. However, I needed my
money class to be as efficient as possible; otherwise who would
use it?
Available tools
===============
My first move was to examine the available tools to handle money
quantities. I looked at the following:
- Borland's C++ BCD class
- Zortech's C++ BCD class
- Zortech's C++ money class
BCD stands for Binary Coded Decimal. We frequently use binary
representation, where a number is stored in a "computer word" as a
sequence of binary "digits" that we call "bits". In BCD, what we
store are the base 10 digits of a number, usually using their
binary values. For example, the number 1234 will be stored in a
pair of bytes using the same bit pattern as the 0x1234 hexadecimal
constant. As most computers use eight bit bytes, each byte will
hold two BCD digits. Many bit patterns are not valid when
interpreted as BCD numbers: 0xFFFF, 0x0A0A, 0x123A, are all
invalid BCD quantities because in decimal notation we can use only
the digits 0...9.
I chose not to use Borland's BCD class for many reasons. It does
not provide specific support to handle money quantities. For
example, whenever you multiply and divide two money variables you
have to figure out what happens with the remaining digits that go
further than the number of cents. The header file in Borland C++
v2.0 sucks in the <iostream.h> header file which slows down
compilation quite a bit. I still don't use streams, and I don't
like to be forced to use them if I don't have to. As much of the
BCD library needs to be loaded when using BCDs, this increases the
size of executable files, which I find unattractive. Finally, the
source code for this class is not included with the compiler (it
is available at an extra charge); I have learned to examine the
source code for a library before using it. The BCD class by
Zortech shares all of these inconveniences.
In the Zortech C++ Tools package a money class is implemented,
where a money quantity is a two member structure:
class money {
long dollars;
int cents;
// ...
};
In most implementations, a long can hold only nine or ten decimal
digits, which means that the range of numbers that this class
permits is smaller the range granted by doubles. Zortech's
implementation is quite clean, but nonetheless bulky (or bulkier
than mine, if you prefer). This class is efficient, because the
operators are implemented using integer arithmetic and each money
variable is quite short.
I know these tools are good enough to handle money quantities.
Perhaps I didn't use them because I didn't write them. We
programmers like to use our own tools, and when the cost of
implementing them is not very big, we usually wind up rewriting
code. I feel also that my experience using scaled doubles in C
made me reluctant to use the available options, because those
implementations were not meant to solve exactly my problem. I
guess to be an inventor one needs to dislike what is already
available...
Also, I wanted my money class to be portable, not compiler vendor
dependent.
Defining requirements
=====================
After examining these classes, I sat down to define the
requirements for my money class. This is what I came up with:
a) Money quantities should behave as regular numbers.
b) The money class should be portable.
c) The programmer should be protected from misusing money
quantities.
d) It should be possible to use standard library functions with
money quantities.
e) Most operators should be inlined, to let the compiler optimize
the generated code.
f) The money header file should be short.
g) The programmer should be able to define the number of decimals
in a money data item.
These requirements are quite natural. Good object oriented
programming dictates that a class represents a concept that we
need to use in a straight-forward way; an efficient implementation
is always welcomed by any C++ programmer. Where I had to give in
was in the amount of storage that a money variable would use:
eight bytes for most computers compared to six (25% less) if one
uses the Zortech implementation.
Listing 1 is the header file "money.h" which defines and
implements the money class. You will find amusing that all the
methods in this class are inline: I did this to fulfill my fifth
requirement. There are many versions of some of the arithmetic
operators to cater for the various cases that are usually found in
real life programs.
The money class permits a programmer to write expressions such as
the following:
money mm,m = 1000; // I've got a thou'
double tax = 0.23; // State Government of Insomnia
m *= (1-tax); // This is what I have
m = m+500; // Thanks, mommy...
mm = 500;
m = m + 1500 - (mm / m) * (1.0/3.0); // etc...
printf("Salary = %10.2f\n",
(double) ((10+mm)/m * m));
In a nutshell, the programmer can freely mix money quantities with
regular numbers to get the correct results. Furthermore, the
compiler will warn the programmer when he tries to misuse money
data items, as in:
money m, mm; // ok
double d = m*mm; // can't multiply moneys
mm = d/m; // can't divide by money
Listing 2 is a test program for the money class. You need to use
your symbolic debugger to see what is going on at each point in
the program.
Implementation details
======================
I tried first to fake money quantities using longs, but the
difficulty in doing so stopped me from pursuing this approach
(note that the Zortech tools money type is implemented using
integer arithmetic).
As I knew that a money variable would be a double, the main
problem to solve implementing this class was to keep track of when
to multiply by the scale factor and when not to. This is
accomplished by each of the overloaded arithmetic operators. I
also implemented the money::FIX() member functions to get rid of
the excess decimals whenever this is needed.
When the preprocesssor constant MONEY_ROUNDING is defined, the
exccess decimals in a double are rounded when a double is assigned
to a money variable. Otherwise the excess decimals are truncated:
money m(1.5199); // $1.52, when MONEY_ROUNDING
money m(1.5199); // $1.51, when not MONEY_ROUNDING
The programmer cannot selectively choose whether to round or not
case by case: it is an all or nothing affair because the decision
must be made at compile time.
The preprocessor constant MONEY_DECIMALS defines how many decimals
a money item has. The member function money::SCALE() is
implemented using a preprocessor trick that returns the scale
factor used to multiply a double to make it a money quantity. If
three decimals are needed for money items, then the scale factor
would be 1,000 = 10^3. In some countries the inflation is so high
that the number of decimals is negative; in this case the scale
factor would be a number less than one. As money::SCALE() is an
inline function, the compiler can optimize out the division by the
scale factor in some cases. If the programmer doesn't define
MONEY_DECIMALS, then a default value of 2 decimals is used. In my
programs, I define MONEY_DECIMALS before including the <money.h>
file:
#define MONEY_DECIMALS 4 // must use a decimal number
#include "money.h" // or TENPOW bombs.
The vector constructor money::money() does not initialize a money
item, because in many cases doing so is wasteful. Money items can
be regarded by the programmer as regular numbers. The compiler
should be able to optimize out this constructor if it is used.
The class money implements most arithmethic operators, but it does
not implement the following:
money operator* (const money&, const money&);
money operator/ (const double, const money&);
It just does not make sense to use them in a program; if you use
them you will get a compile time error (a disatisfied programmer
could add them to the class easily).
A careful examination of the implementation of each of the
arithmetic operators in file "money.h" will show that the class is
programmed to minimze the number of times that each double needs
to be scaled up by the scale factor.
The comparison operators are defined only for money items. This
means that when a money data type is compared to a double, the
compiler will promote the double to a money variable using the
constructor money::money(double). If this is not what the
programmer intended, an explicit typecast can be used:
double d = 15.253; // 15.253
money m = 15.25; // $ 15.25
if (d == m) { // TRUE: d becomes money(d)
}
if (d == (double) m) { // FALSE: 15.253 != 15.25
}
The function flatten(money, cents, rounding) is very useful to
round up a money quantity to the nearest value that can be paid
with coins. For example, in Costa Rica there are no one cent
coins, because the smallest coin is worth 25 cents of a colón
(which we call a "peseta"). In the following example a money item
is rounded up to pesetas:
money m = 125.80; // 125.88 colones
money mm = flatten(m); // 125.75 colones
The complete money class is implemented in the header file called
"money.h" using inline functions, to permit the compiler to
optimize out any floating point operations when it can. I decided
not to provide more operators for money items, because the type
converter money::operator double() allows the standard math
functions to be used with money items. The header file <money.h>
includes only two files, <math.h> and <float.h>, which have the
prototypes for functions floor(), ceil(), fmod(), and DBL_DIG. If
you want to, you can declare every double variable in "money.h" as
a long double, to use bigger money quantities.
As implemented, the money class should be quite portable because
it does not make use of any odd C++ constructs. Depending on the
compiler being used, it is quite possible to optimize out many of
the inline operators, and thus yield efficient programs.
Conclusion
==========
The money class is a tiny C++ class that lets the programmer use
money items with ease. The implementation is as efficient as using
floating point values in arithmetic expressions. There is no
reason to prevent you from using it right away.
Small is beautiful.