home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / C / MONEY / MONEY.VDE < prev   
Text File  |  1993-08-09  |  14KB  |  332 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.                    Yet Another C++ Money Class
  7.  
  8.                                por
  9.  
  10.                           Adolfo Di Mare
  11.                       BITNET: adimare@UCRVM2
  12.                           (506) 53-4853
  13.  
  14.                    Reporte Técnico PIBDC-02-91
  15.                            Revisión 1.0
  16.                        Proyecto 326-89-019
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25. Resumen:  Se  presenta  una  eficiente  y simple  clase  C++  para 
  26. manipular  datos que representan dinero.  La clase  se  implementa 
  27. usando números de punto flotante dobles.
  28.  
  29.  
  30.  
  31.  
  32. Abstract:  An  efficient  and  simple C++ class  to  handle  money 
  33. quantities  is  presented.   The  class  uses  scaled  doubles  to 
  34. represent money data variables.
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41. Adolfo  Di Mare is Associate professor at the Universidad de Costa 
  42. Rica,  where he teaches programming. His research interest are the 
  43. use of Turbo Pascal and C++ to solve information system problems.
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50. Esta investigación se realizó dentro del proyecto de investigación 
  51. 326-89-019 "Estudios de la Tecnología de Programación por  Objetos 
  52. y  C++",  inscrito  ante la Vicerrectoría de Investigación  de  la 
  53. Universidad   de  Costa  Rica.   La  Escuela  de  Ciencias  de  la 
  54. Computación e Informática también ha aportado fondos para realizar 
  55. este trabajo.
  56. .FO     money.h                   #              Adolfo Di Mare
  57.                    Yet Another C++ Money Class
  58.                    ===========================
  59.  
  60. I  use  C  to program information  systems  where  handling  money 
  61. quantities  is  quite  common.  Using doubles to  represent  money 
  62. quantities   is  not  good  enough,   because  decimals  are   not 
  63. represented  correctly  and sometimes round-off  errors  occur.  A 
  64. while  ago a friend of mine gave me a very good idea  to  overcome 
  65. this problem:  he works using Canon BASIC,  and he has always used 
  66. floor(double*100.0)  to represent money quantities.  Any amount is 
  67. represented as a double with no fractional part.   With this trick 
  68. no decimals are lost because the double type,  with its 15+  digit 
  69. precision,  is used as a compiler supported long long. You need to 
  70. be careful when multiplying and dividing, but in many applications 
  71. these operations are rare.
  72.  
  73. For example,  $25.35 is represented as the double 2535.0,  using a 
  74. scale  factor of 100 (for two decimal places).  The problem I kept 
  75. facing  in my C programs was to remember when to multiply  by  100 
  76. and  when not to.  For example,  to add two doubles that represent 
  77. money quantities there is no need to multiply by 100:
  78.  
  79.     $25.35 + $35.75 <==> 2535.0 + 3575.0 == 6110.0 <==> $61.10
  80.  
  81. Also,   one  should  never  multiply  by  the  scale  factor  when 
  82. multiplying a money quantity:
  83.  
  84.    $100 * (1.0+0.06) <==> 10000.0 * (1.06) == 10600.0 <==> $106
  85.  
  86. It is just too easy to forget to multiply by the scale factor when 
  87. using  doubles as money.  We human beings forget too much,  so  we 
  88. need the computer to remember for us.
  89.  
  90. When  I started programming in C++,  I realized that a  C++  money 
  91. class  was the solution for these problems.  However,  I needed my 
  92. money  class to be as efficient as possible;  otherwise who  would 
  93. use it?
  94.  
  95. Available tools
  96. ===============
  97.  
  98. My  first move was to examine the available tools to handle  money 
  99. quantities. I looked at the following:
  100.  
  101.      - Borland's C++ BCD class
  102.      - Zortech's C++ BCD class
  103.      - Zortech's C++ money class
  104.  
  105. BCD  stands  for Binary Coded Decimal.  We frequently  use  binary 
  106. representation, where a number is stored in a "computer word" as a 
  107. sequence of binary "digits" that we call "bits".  In BCD,  what we 
  108. store  are  the base 10 digits of a number,  usually  using  their 
  109. binary values.  For example,  the number 1234 will be stored in  a 
  110. pair of bytes using the same bit pattern as the 0x1234 hexadecimal 
  111. constant.  As  most computers use eight bit bytes,  each byte will 
  112. hold  two  BCD  digits.  Many  bit patterns  are  not  valid  when 
  113. interpreted  as BCD  numbers:  0xFFFF,  0x0A0A,  0x123A,  are  all 
  114. invalid BCD quantities because in decimal notation we can use only 
  115. the digits 0...9.
  116.  
  117. I  chose not to use Borland's BCD class for many reasons.  It does 
  118. not  provide  specific support to  handle  money  quantities.  For 
  119. example,  whenever you multiply and divide two money variables you 
  120. have  to figure out what happens with the remaining digits that go 
  121. further than the number of cents.  The header file in Borland  C++ 
  122. v2.0  sucks  in  the  <iostream.h> header file  which  slows  down 
  123. compilation quite a bit.  I still don't use streams,  and I  don't 
  124. like  to be forced to use them if I don't have to.  As much of the 
  125. BCD library needs to be loaded when using BCDs, this increases the 
  126. size of executable files,  which I find unattractive. Finally, the 
  127. source  code for this class is not included with the compiler  (it 
  128. is  available at an extra charge);  I have learned to examine  the 
  129. source  code  for  a library before using it.  The  BCD  class  by 
  130. Zortech shares all of these inconveniences.
  131.  
  132. In  the  Zortech C++ Tools package a money class  is  implemented, 
  133. where a money quantity is a two member structure:
  134.  
  135.     class money {
  136.         long dollars;
  137.         int  cents;
  138.         // ...
  139.     };
  140.  
  141. In most implementations,  a long can hold only nine or ten decimal 
  142. digits,  which  means  that the range of numbers that  this  class 
  143. permits  is  smaller  the  range  granted  by  doubles.  Zortech's 
  144. implementation is quite clean,  but nonetheless bulky (or  bulkier 
  145. than mine,  if you prefer).  This class is efficient,  because the 
  146. operators  are implemented using integer arithmetic and each money 
  147. variable is quite short.
  148.  
  149. I  know  these tools are good enough to handle  money  quantities. 
  150. Perhaps  I  didn't  use  them because  I  didn't  write  them.  We 
  151. programmers  like  to  use our own tools,  and when  the  cost  of 
  152. implementing  them is not very big,  we usually wind up  rewriting 
  153. code.  I  feel also that my experience using scaled doubles  in  C 
  154. made  me  reluctant to use the available  options,  because  those 
  155. implementations  were  not meant to solve exactly  my  problem.  I 
  156. guess  to  be  an inventor one needs to dislike  what  is  already 
  157. available...
  158.  
  159. Also,  I wanted my money class to be portable, not compiler vendor 
  160. dependent.
  161.  
  162. Defining requirements
  163. =====================
  164.  
  165. After   examining  these  classes,   I  sat  down  to  define  the 
  166. requirements for my money class. This is what I came up with:
  167.  
  168. a) Money quantities should behave as regular numbers.
  169. b) The money class should be portable.
  170. c)  The  programmer  should  be  protected  from  misusing   money 
  171. quantities.
  172. d)  It  should be possible to use standard library functions  with 
  173. money quantities.
  174. e) Most operators should be inlined,  to let the compiler optimize 
  175. the generated code.
  176. f) The money header file should be short.
  177. g) The programmer should be able to define the number of  decimals 
  178. in a money data item.
  179.  
  180. These  requirements  are  quite  natural.   Good  object  oriented 
  181. programming  dictates  that a class represents a concept  that  we 
  182. need to use in a straight-forward way; an efficient implementation 
  183. is  always welcomed by any C++ programmer.  Where I had to give in 
  184. was  in  the amount of storage that a money  variable  would  use: 
  185. eight  bytes for most computers compared to six (25% less) if  one 
  186. uses the Zortech implementation.
  187.  
  188. Listing   1  is  the  header  file  "money.h"  which  defines  and 
  189. implements  the money class.  You will find amusing that  all  the 
  190. methods  in this class are inline:  I did this to fulfill my fifth 
  191. requirement.  There  are many versions of some of  the  arithmetic 
  192. operators to cater for the various cases that are usually found in 
  193. real life programs.
  194.  
  195. The money class permits a programmer to write expressions such  as 
  196. the following:
  197.  
  198.     money  mm,m = 1000;    // I've got a thou'
  199.     double tax  = 0.23;    // State Government of Insomnia
  200.     m *= (1-tax);          // This is what I have
  201.     m  = m+500;            // Thanks, mommy...
  202.     mm = 500;
  203.     m  = m + 1500 - (mm / m) * (1.0/3.0); // etc...
  204.     printf("Salary = %10.2f\n",
  205.            (double) ((10+mm)/m * m));
  206.  
  207. In a nutshell, the programmer can freely mix money quantities with 
  208. regular  numbers  to get the  correct  results.  Furthermore,  the 
  209. compiler  will  warn the programmer when he tries to misuse  money 
  210. data items, as in:
  211.  
  212.     money  m, mm;     // ok
  213.     double d = m*mm;  // can't multiply moneys
  214.     mm = d/m;         // can't divide by money
  215.  
  216. Listing  2 is a test program for the money class.  You need to use 
  217. your  symbolic debugger to see what is going on at each  point  in 
  218. the program.
  219.  
  220. Implementation details
  221. ======================
  222.  
  223. I  tried  first  to fake money quantities  using  longs,  but  the 
  224. difficulty  in  doing  so stopped me from pursuing  this  approach 
  225. (note  that  the  Zortech tools money type  is  implemented  using 
  226. integer arithmetic).
  227.  
  228. As  I  knew  that a money variable would be  a  double,  the  main 
  229. problem to solve implementing this class was to keep track of when 
  230. to  multiply  by  the  scale factor  and  when  not  to.  This  is 
  231. accomplished  by  each of the overloaded arithmetic  operators.  I 
  232. also  implemented the money::FIX() member functions to get rid  of 
  233. the excess decimals whenever this is needed.
  234.  
  235. When  the preprocesssor constant MONEY_ROUNDING  is  defined,  the 
  236. exccess decimals in a double are rounded when a double is assigned 
  237. to a money variable. Otherwise the excess decimals are truncated:
  238.  
  239.     money  m(1.5199);  // $1.52, when MONEY_ROUNDING
  240.     money  m(1.5199);  // $1.51, when not MONEY_ROUNDING
  241.  
  242. The  programmer cannot selectively choose whether to round or  not 
  243. case by case:  it is an all or nothing affair because the decision 
  244. must be made at compile time.
  245.  
  246. The preprocessor constant MONEY_DECIMALS defines how many decimals 
  247. a   money  item  has.   The  member  function  money::SCALE()   is 
  248. implemented  using  a  preprocessor trick that returns  the  scale 
  249. factor used to multiply a double to make it a money  quantity.  If 
  250. three  decimals are needed for money items,  then the scale factor 
  251. would be 1,000 = 10^3.  In some countries the inflation is so high 
  252. that  the number of decimals is negative;  in this case the  scale 
  253. factor  would be a number less than one.  As money::SCALE() is  an 
  254. inline function, the compiler can optimize out the division by the 
  255. scale  factor  in some cases.  If the  programmer  doesn't  define 
  256. MONEY_DECIMALS,  then a default value of 2 decimals is used. In my 
  257. programs,  I  define MONEY_DECIMALS before including the <money.h> 
  258. file:
  259.  
  260.     #define   MONEY_DECIMALS 4  // must use a decimal number
  261.     #include "money.h"          // or TENPOW bombs.
  262.  
  263. The vector constructor money::money() does not initialize a  money 
  264. item,  because in many cases doing so is wasteful. Money items can 
  265. be  regarded  by the programmer as regular numbers.  The  compiler 
  266. should be able to optimize out this constructor if it is used.
  267.  
  268. The class money implements most arithmethic operators, but it does 
  269. not implement the following:
  270.  
  271.           money operator* (const money&, const money&);
  272.           money operator/ (const double, const money&);
  273.  
  274. It just does not make sense to use them in a program;  if you  use 
  275. them  you will get a compile time error (a disatisfied  programmer 
  276. could add them to the class easily).
  277.  
  278. A  careful  examination  of  the implementation  of  each  of  the 
  279. arithmetic operators in file "money.h" will show that the class is 
  280. programmed  to minimze the number of times that each double  needs 
  281. to be scaled up by the scale factor.
  282.  
  283. The  comparison operators are defined only for money  items.  This 
  284. means  that  when a money data type is compared to a  double,  the 
  285. compiler  will  promote the double to a money variable  using  the 
  286. constructor  money::money(double).   If  this  is  not  what   the 
  287. programmer intended, an explicit typecast can be used:
  288.  
  289.     double d = 15.253;     //   15.253
  290.     money  m = 15.25;      // $ 15.25
  291.  
  292.     if (d == m) {          // TRUE:  d becomes money(d)
  293.     }
  294.     if (d == (double) m) { // FALSE: 15.253 != 15.25
  295.     }
  296.  
  297. The  function flatten(money,  cents,  rounding) is very useful  to 
  298. round  up  a money quantity to the nearest value that can be  paid 
  299. with  coins.  For  example,  in Costa Rica there are no  one  cent 
  300. coins,  because  the  smallest coin is worth 25 cents of  a  colón 
  301. (which we call a "peseta").  In the following example a money item 
  302. is rounded up to pesetas:
  303.  
  304.     money m  = 125.80;      //  125.88 colones
  305.     money mm = flatten(m);  //  125.75 colones
  306.  
  307. The complete money class is implemented in the header file  called 
  308. "money.h"  using  inline  functions,  to permit  the  compiler  to 
  309. optimize out any floating point operations when it can.  I decided 
  310. not  to provide more operators for money items,  because the  type 
  311. converter   money::operator  double()  allows  the  standard  math 
  312. functions to be used with money items.  The header file  <money.h> 
  313. includes  only two files,  <math.h> and <float.h>,  which have the 
  314. prototypes for functions floor(),  ceil(), fmod(), and DBL_DIG. If 
  315. you want to, you can declare every double variable in "money.h" as 
  316. a long double, to use bigger money quantities.
  317.  
  318. As implemented,  the money class should be quite portable  because 
  319. it  does not make use of any odd C++ constructs.  Depending on the 
  320. compiler being used,  it is quite possible to optimize out many of 
  321. the inline operators, and thus yield efficient programs.
  322.  
  323. Conclusion
  324. ==========
  325.  
  326. The  money class is a tiny C++ class that lets the programmer  use 
  327. money items with ease. The implementation is as efficient as using 
  328. floating  point  values in arithmetic  expressions.  There  is  no 
  329. reason to prevent you from using it right away.
  330.  
  331.                        Small is beautiful.
  332.