home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / decima.zip / DECIMAL.CLS
Text File  |  1995-04-17  |  11KB  |  340 lines

  1. //**************    Begin Decimal.hpp     **************************************
  2. //*****************************************************************************
  3. /* (C) Copyright Stephen B. Behman, 1994 -- All rights reserved. */
  4. // $Header: h:/include/RCS/decimal.hpp 2.0 94/06/24 05:47:25 Steve Exp $
  5.  
  6. #ifndef __Decimal__HPP
  7.   #define __Decimal__HPP
  8.  
  9. #include <math.h>
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12.  
  13. #ifndef _STR_
  14.   #include "str.hpp"
  15. #endif
  16.  
  17. //  I have used this class to sum 150k "decimals" fetched from a DB2/2 database
  18. //  (using embedded SQL) one at a time and get exactly the same result as the
  19. //  SQL query:
  20.  
  21. //  "select sum( amount ) from table"  which sum I assume to be "correct."
  22.  
  23. //  ************ NOTE *********************************
  24. //  I have "tricked" SQLPREP into accepting "Decimal" and "Str" as data types.
  25. //  but that is another story.
  26.  
  27.  
  28. //  This code is certainly inelegant but "it works for me" -- I make no
  29. //  warranties for it.
  30.  
  31.  
  32. //  You may use this code as you wish with the proviso: IF YOU IMPROVE IT
  33. //  YOU MUST SHARE THE IMPROVEMENTS WITH ME AND THE OTHERS WHO ARE USING IT!
  34.  
  35. //  To fulfill your obligation to share improvements send them to me:
  36. //  Steve Behman [76360,3153] on compuserve
  37.  
  38. //  I encourage you to send me whatever feedback you can.  Negative feedback
  39. //  is actually more valuable than positive, but nobody was ever hurt by an
  40. //  "ataboy."
  41.  
  42. class Money;
  43.  
  44. class Decimal
  45.   {
  46.   public:
  47.    double val;
  48.    unsigned long scale;
  49.  
  50. //  some constructors -- the ones I have found I need!
  51. //  Default scale is 8 -- possibly 2 would be better.
  52.  
  53.    Decimal( const double d=0.0, const unsigned long s=8 )
  54.      : scale( s>17 ? 17 : s )
  55.      {
  56.       if( d == 0.0 ){ val=0.0; }
  57.       else                  // store as "almost int"
  58.     {
  59.      if( d > 0.0 ) modf( d*tab[17+scale]+0.5, &val ); // make "integer"
  60.      else modf( d*tab[scale+17]-0.5, &val );
  61.      val*=tab[17-scale];          // store as double
  62.     }
  63.      }
  64.  
  65.    Decimal( const unsigned long v, const unsigned long s=8 )
  66.        : val( v ), scale( s<17 ? s : 17 ){}
  67.  
  68.  
  69.    Decimal( const char * const s )
  70.      {                      // Let the string tell what the scale is
  71.       scale=0;
  72.       if( s && s[0] )
  73.     {
  74.      int wasi=0;
  75.      for( int i=0; s[i]; i++ )
  76.        {
  77.         if( s[i]=='.' ) wasi=1;
  78.         scale+=wasi;
  79.        }
  80.      scale=( wasi>0 ? scale-1 : 0 );
  81.      *this=Decimal( atof(s), scale );
  82.     }
  83.       else
  84.     {
  85.      val=0.0;
  86.      scale=0;
  87.     }
  88.      }
  89.  
  90.    operator double&(){return val;}    // returns an "almost integer"
  91.  
  92. //  This is the "biggie" -- assignment causes rounding before it stores
  93.  
  94.    Decimal& operator =( const Decimal& d )
  95.      {
  96.       if( d.val == 0.0 ){ val=0.0; }
  97.       else
  98.     {
  99.      if( d.val > 0.0 ) modf( d.val*tab[17+scale]+0.5, &val );
  100.      else modf( d.val*tab[scale+17]-0.5, &val );
  101.      val*=tab[17-scale];
  102.     }
  103.       return *this;
  104.      }
  105.  
  106.    Decimal  operator-(){ return Decimal(-val, scale ); }
  107.  
  108. //  these prevent accumulation of errors by rounding sum,prod, etc during calc
  109.    Decimal& operator =( const double d ){ return *this=Decimal( d, scale ); }
  110.    Decimal& operator+=( const double d ){ return *this=Decimal( val+d, scale);}
  111.    Decimal& operator-=( const double d ){ return *this=Decimal( val-d, scale);}
  112.    Decimal& operator/=( const double d ){ return *this=Decimal( val/d, scale);}
  113.    Decimal& operator*=( const double d ){ return *this=Decimal( val*d, scale);}
  114.  
  115. //  this one rounds during a calculation it is used as follows:
  116. //  1) dec=( (qty*fractional_pennies) | 2 )*discount;
  117. //  versus
  118. //  2) dec=qty*fractional_pennies*discount;
  119.  
  120. //  1 applies discount to the rounded product of price and qty
  121. //  2 rounds the product on assignment
  122. //  1 & 2 would tend to differ for large values of qty
  123.  
  124.  Decimal  operator |( unsigned s ){ return Decimal( val, s ); }
  125.  Decimal& resize( long l ){ return *this=Decimal( val, l ); }
  126.  
  127.  
  128.   private:
  129. //  Table of 10**-17 to 10**17 for "scaling"
  130.    static const double tab[35];
  131.   };
  132.  
  133.  
  134. class Money : public Decimal
  135.   {
  136.   public:
  137.    Money( double d=0.0 ):Decimal( d, 2 ){}
  138.    Money( Decimal& d ):Decimal( d | 2 ){}
  139.    Money( const char * const s ) : Decimal( s )
  140.      {
  141.        this->resize( 2 );
  142.      }
  143.    Money operator-(){ return Money( -val );}
  144.    Money& operator=( const double d ){return *this=Decimal( d, 2 );}
  145.    Money& operator=( const Decimal& d ) { return *this=Decimal( d.val, 2 ); }
  146.    Str asWords( );
  147.   };
  148.  
  149.  
  150. //  look at the "Str" class in Str.hpp (below) file for this one
  151.  
  152.  
  153. inline Str::Str( const Decimal& d )
  154.   {
  155.    char b[32];
  156.    int i=sprintf( b, "%.*f", d.scale, d.val );
  157.    *this=Str( (const void*) b, i );
  158.   }
  159. #endif
  160.  
  161. //**************    End Decimal.hpp    ****************************************
  162. //*****************************************************************************
  163.  
  164.  
  165. //**************    Begin Decimal.cpp     **************************************
  166. //*****************************************************************************
  167. //  This is decimal.cpp which should be compiled and the .obj linked into
  168. //  whatever uses the Decimal class
  169. /* (C) Copyright Stephen B. Behman, 1994 -- All rights reserved. */
  170. // $Header: h:/lib/RCS/decimal.cpp 2.0 94/06/24 05:54:08 Steve Exp $
  171.  
  172. #include "decimal.hpp"
  173.  
  174. static const char *ones[10]={"","ONE ","TWO ","THREE ","FOUR ","FIVE ","SIX ","SEVEN ","EIGHT ","NINE "};
  175. static const char *teens[10]={"","ELEVEN ","TWELVE ","THIRTEEN ","FOURTEEN ","FIFTEEN ","SIXTEEN ","SEVENTEEN ","EIGHTEEN ","NINETEEN "};
  176. static const char *tens[10]={"","TEN","TWENTY","THIRTY","FORTY","FIFTY","SIXTY","SEVENTY","EIGHTY","NINETY"};
  177.  
  178. static Str th( long v )
  179.    {
  180.      Str out("");
  181.      long hun,ten;
  182.      ten=v%100;
  183.      hun=(v-ten)/100;
  184.      if(hun) out+= Str(ones[hun]) + "HUNDRED ";
  185.      if(ten > 10 && ten <20 ) out+= teens[ten-10];
  186.      else
  187.        {
  188.     out+=tens[ten/10];
  189.     out+=( ten%10 && ten > 9 ) ? "-" : "";
  190.     out+=ones[ten%10];
  191.        }
  192.     return out.strip();
  193.    }
  194.  
  195.  
  196. Str Money::asWords( )
  197.   {
  198.    Str out("");
  199.    if( val==0 ) return out;
  200.    double whole;
  201.    long cents=( long )( 100.00001*modf( val>0.0 ? val : -val, &whole ) );
  202.    long more=( long )whole;
  203.    out="";
  204.    if( more > 999999 ){ out="Illegal"; return out; }
  205.    if( more > 999 )
  206.      {
  207.       out+=th( more/1000 )+ " THOUSAND ";
  208.       more%=1000;
  209.      }
  210.    out+=th( more );
  211.    if( whole == 0 && cents ) out="ZERO";
  212.    if( cents ) out+=Str(" and ")+Str( cents )+"/100 DOLLARS";
  213.    else out+= Str( " DOLLAR" )
  214.           +(whole != 1.0 && whole != 0.0 ? "S" : "") + " EVEN";
  215.    return out;
  216.   }
  217.  
  218. const double Decimal::tab[35]={1.0E-17,1.0E-16,1.0E-15,1.0E-14,1.0E-13,1.0E-12,1.0E-11,1.0E-10,1.0E-9,1.0E-8,1.0E-7,1.0E-6,1.0E-5,1.0E-4,1.0E-3,1.0E-2,1.0E-1,1.0E0, 1.0E1, 1.0E2,1.0E3,1.0E4,1.0E5,1.0E6, 1.0E7, 1.0E8, 1.0E9, 1.0E10, 1.0E11, 1.0E12,1.0E13,1.0E14,1.0E15,1.0E16, 1.0E17 };
  219.  
  220. //**************    End  Decimal.cpp     **************************************
  221. //*****************************************************************************
  222.  
  223.  
  224.  
  225. //**************    Begin Str.hpp    ******************************************
  226. //*****************************************************************************
  227. //  The stuff below is my "Str.hpp"
  228. //  This defines the Str class which extends IString to some of the places
  229. //  where I wanted it to go
  230.  
  231. #ifndef _ISTRING_
  232.   #include <IString.hpp>
  233. #endif /* _ISTRING_ */
  234.  
  235. #ifndef _STR_
  236.   #define _STR_
  237.  
  238.       #pragma info( nocnv )
  239. #define substr subString          // I hate reinventing the wheel
  240.  
  241. class Str : public IString
  242.  {
  243.   friend class Money;
  244.   friend class Decimal;
  245.   public:
  246.   Str():IString(){}
  247.   Str( const IString &a ):IString( a ){}
  248.   Str( int a ):IString( a ){}
  249.   Str( unsigned a ):IString( a ){}
  250.   Str( long a ):IString( a ){}
  251.   Str( unsigned long a):IString( a ){}
  252.   Str( short a):IString( a ){}
  253.   Str( unsigned short a):IString( a ){}
  254.   Str( double a):IString( a ){}
  255.   Str( char a):IString( a ){}
  256.   Str( unsigned char a):IString( a ){}
  257.   Str( signed char a):IString( a ){}
  258.   Str( const char * a):IString( a ){}
  259.   Str( const unsigned char * a):IString( a ){}
  260.   Str( const signed char * a):IString( a ){}
  261.   Str( const void * a, unsigned b, char padCharacter = ' ' )
  262.     :IString( a,b,padCharacter ){}
  263.   Str( const void *g, unsigned a, const void  * b, unsigned c, char p=' ' )
  264.     :IString( g,a,b,c,p ){}
  265.   Str( const void *a, unsigned b, const void *c, unsigned d, const void *e,
  266.        unsigned f, char p = ' ' ):IString( a,b,c,d,e,f,p){}
  267.  
  268. //  I do not know why IString was set up as it was but the following
  269. //  "casts" work for me
  270.   operator double(){ return this->asDouble(); }
  271.   operator unsigned long(){ return this->asUnsigned(); }
  272.   operator long(){ return ( long )this->asInt(); }
  273.   operator const void*() const { return ( const void* )( char* )*this; }
  274.  
  275.   Str word( long l, char a  )
  276.     {
  277.      Str t=*this;
  278.      unsigned i, j;
  279.      if( !l ) return t;
  280.      j=t.indexOf( a );
  281.      for( i=1; i<l && j; i++ )
  282.        {
  283.     if( j ) t=t.substr( j+1 );
  284.     j=t.indexOf( a );
  285.        }
  286.       if( i < l ) return Str();
  287.       if( j ) return t.substr( 1, j-1 ).strip();
  288.       return t.strip();
  289.     }
  290.  
  291.   Boolean isNumeric()
  292.     {
  293.      char *s=*this;
  294.      while( *s && ( *s <= '9' && *s >= '0' ) || *s == '.' || *s == '-' ) s++;
  295.      return !*s;
  296.     }
  297.  
  298.   Str& unZero()
  299.     {
  300.      char *s=( char* )this;
  301.      int i=this->length();
  302.      for( int j=0; j<i; j++ ) if( s[j]==0 ) s[j]='~';
  303.      return *this;
  304.     }
  305.  
  306.   Str& reZero()
  307.     {
  308.      char *s=( char* )this;
  309.      int i=this->length();
  310.      for( int j=0; j<i; j++ ) if( s[j]=='~' ) s[j]=0;
  311.      return *this;
  312.     }
  313.  
  314.   /*--------------------------------------------------------------------------
  315.     The function asReceiver( unsigned size ) works like operator char* but the
  316.     length of the calling IString is extended (if it need be) to the length
  317.     specified in the size argument.
  318.   ---------------------------------------------------------------------------*/
  319.  
  320.   char* asReceiver( long l )
  321.     {
  322.      return *this=IString( (char*) *this, (short)l, '\0' );
  323.     }
  324.  
  325.   Str& cStr()
  326.     {
  327.      int i=this->indexOf( '\0' );
  328.      return *this=IString( ( char * )*this, i-1 );
  329.     }
  330.  
  331. //  A constructor which gives the "right" number of decimal places
  332.   Str( const Decimal& d );
  333.  
  334.  };
  335.  
  336.       #pragma info( restore )
  337. #endif
  338. //**************    End  Str.hpp     **************************************
  339. //*****************************************************************************
  340.