Click here to Skip to main content
11,798,546 members (64,354 online)
Click here to Skip to main content

A Decimal Class Implementation

, 16 Feb 2003 153.7K 1.1K 22
Rate this:
Please Sign up or sign in to vote.
Use this class when high precision is required in basic numerical operations.


In my last article[^], I eluded to the fact that there was another bug hiding in my code. Well, here it is! I guess it isn't precisely a bug, except under certain conditions, such as working with currency, in which precision is required.

Math Precision

One of the problems in the financial world is dealing with numeric precision. The double data type doesn't quite cut it, as we shall see. This presents a problem in C++ which has no decimal data type as found in C#. For example, the following C# code results in a "true" evaluation of n==201:

decimal n=.3M;
if (n==201) ...   // TRUE!

whereas using the double type in C++ does not:

double n=.3;
if (n==201) ...   // FALSE!

This is an important issue when dealing with financial math.

The Decimal Class

To solve this problem, I created a Decimal class. Now, I looked high and low on Code Project and Google searches for something like this, and I didn't find anything, so if I missed a contribution by another person regarding this issue, then I apologize in advance.

class Decimal

        static void Initialize(int precision);

        Decimal(AutoString num);
        Decimal(const Decimal& d);
        Decimal(const __int64 n);
        Decimal(int intPart, int fractPart);

        virtual ~Decimal(void);

        Decimal operator+(const Decimal&);
        Decimal operator-(const Decimal&);
        Decimal operator*(const Decimal&);
        Decimal operator/(const Decimal&);

        Decimal operator +=(const Decimal&);
        Decimal operator -=(const Decimal&);
        Decimal operator *=(const Decimal&);
        Decimal operator /=(const Decimal&);

        bool operator==(const Decimal&) const;
        bool operator!=(const Decimal&) const;
        bool operator<(const Decimal&) const;
        bool operator<=(const Decimal&) const;
        bool operator>(const Decimal&) const;
        bool operator>=(const Decimal&) const;

        CString ToString(void) const;
        double ToDouble(void) const;


        __int64 n;

        static int precision;
        static __int64 q;
        static char* pad;

This is a pretty basic implementation. A static Initialize method is used to set up the desired precision of the class, for all instances of Decimal. Internally, a few helper variables are initialized, which are used elsewhere for string to Decimal conversions and the multiplication and division operators:

void Decimal::Initialize(int prec)
    // create an array of 0's for padding
    pad=new char[precision+1];
    memset(pad, '0', precision);
    // get fractional precision
    q=(__int64)pow(10.0, (double)prec);        

A Microsoft specific 64 bit integer is used to maintain both integer and fractional components of the value, using the __int64 data type. This is a non-ANSII standard type. If you want a 96 bit integer instead, you can modify my class with PJ Naughter's 96 bit integer class found here [^].

String To __int64 Conversion

Decimal::Decimal(AutoString num)
    // get the integer component
    AutoString intPart=num.LeftEx('.');
    // get the fractional component
    AutoString fractPart=num.RightEx('.');

    // "multiply" the fractional part by the desired precision

    // create the 64bit integer as a composite of the
    // integer and fractional components.

The conversion from a string to a 64 bit integer is interesting to look at as it reveals the internal workings of the class. First, the integer part and fractional parts are separated from the number. The AutoString class is a CString derived class and provides a bit nicer interface for these kind of things.

For example, given "123.456":

AutoString intPart=num.LeftEx('.');
AutoString fractPart=num.RightEx('.');

Now let's say you've initialized the class with a precision of 4 digits past the decimal point. This creates a pad string of "0000" in the initialization function, which is used to determine how many zeros to append to the fractional string. In code:


fractPart is appended with a single "0" and becomes "4560".

Finally, the two components, the integer and fractional components, are combined by shifting (base 10) the integer component left by the fractional precision and adding the fractional component:


The result is a single integer which maintains both integer and fractional components. Because all Decimal "numbers" are normalized in this way, the four basic operations (+, -, *, /) are trivial to implement.

__int64 To String Conversion

CString Decimal::ToString(void) const
    char s[64];
    __int64 n2=n/q;
    int fract=(int)(n-n2*q);
    sprintf(s, "%d.%0*d", (int)n2, precision, fract);
    return s;

Again, this code reveals the internal workings of the Decimal class. The 64 bit value is shifted right (base 10) by the precision and the integer component is extracted:

__int64 n2=n/q;

The fractional component is extracted by shifting left the integer component and subtracting from the original value:

int fract=(int)(n-n2*q);

And finally the string is constructed. Note the use of the * directive which tells the printf routine to determine the precision of the integer from the variable list:

sprintf(s, "%d.%0*d", (int)n2, precision, fract);


double n=.3;

printf("n=%.04lf  (int)n=%d\r\n", n, (int)n);
printf("n == 201 ? %s\r\n", n==201 ? "yes" : "no");
printf("n >= 201 ? %s\r\n", n>=201 ? "yes" : "no");

Decimal dec(".3");

printf("dec=%s\r\n", dec.ToString());
printf("dec == 201 ? %s\r\n", dec==Decimal("201") ? "yes" : "no");
printf("dec >= 201 ? %s\r\n", dec>=Decimal("201") ? "yes" : "no");

The above is an example usage and produces the following output:

Because the __int64 type is composed from two int types (and decomposed into int types when converted back to a string), it is limited in range to the same values as a signed four byte number, +/- 2^31, or +/-2,147,483,648.

Also, this code is not "internationalized".



This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Marc Clifton
United States United States
Marc is the creator of two open source projects, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website,, where you will find many of his articles and his blog.

Marc lives in Philmont, NY.

You may also be interested in...

Comments and Discussions

QuestionHow to deal with multiplication/division? Pin
DimoneSem10-Aug-15 2:30
memberDimoneSem10-Aug-15 2:30 
Questionand what about the Automation DECIMAL struct Pin
abo hashem12-Sep-13 1:06
memberabo hashem12-Sep-13 1:06 
AnswerRe: and what about the Automation DECIMAL struct Pin
Marc Clifton12-Sep-13 1:17
protectorMarc Clifton12-Sep-13 1:17 
GeneralRe: and what about the Automation DECIMAL struct Pin
brekehan12-Nov-14 12:56
memberbrekehan12-Nov-14 12:56 
GeneralRe: and what about the Automation DECIMAL struct Pin
Marc Clifton12-Nov-14 13:11
protectorMarc Clifton12-Nov-14 13:11 
GeneralMy vote of 1 Pin
Byron Goodman4-Jul-12 12:34
memberByron Goodman4-Jul-12 12:34 
GeneralAnother implementation for C++ Pin
Member 9427193-Jan-11 13:31
memberMember 9427193-Jan-11 13:31 
GeneralThis code needs a health warning Pin
cslocombe27-Jul-08 20:29
membercslocombe27-Jul-08 20:29 
GeneralRe: This code needs a health warning Pin
Marc Clifton28-Jul-08 0:43
protectorMarc Clifton28-Jul-08 0:43 
GeneralRe: This code needs a health warning Pin
brekehan12-Nov-14 12:58
memberbrekehan12-Nov-14 12:58 
GeneralBoost.Rational Pin
Gast1284-Feb-08 4:02
memberGast1284-Feb-08 4:02 
Generalcompare floats Pin
jswoofer13-Oct-06 3:18
memberjswoofer13-Oct-06 3:18 
QuestionWhy do we need decimal? Pin
Andrew Phillips8-Dec-04 15:14
memberAndrew Phillips8-Dec-04 15:14 
AnswerRe: Why do we need decimal? Pin
Marc Clifton8-Dec-04 15:56
protectorMarc Clifton8-Dec-04 15:56 
GeneralRe: Why do we need decimal? Pin
Andrew Phillips13-Dec-04 15:11
memberAndrew Phillips13-Dec-04 15:11 
> Isn't that a good reason to use the decimal type, rather than writing SQL statements ...

I don't know a lot about SQL but again I think converting to an int (1234) would be better.

Now that I think about it it seems that one major reason for inventing a System.Decimal type is so you can write code like:

decimal balance = 2.00; // $2

rather than:

long balance = 200000; // $2 to 000's of cent

ie, for programmers convenience and to avoid bugs caused by miscounting decimal places.

However, creating the decimal type for essentially a compile-time problem is not a good idea. I don't know much about atributes but it would seem that this would be an ideal application for them:

[DecimalPlaces(5)] long balance = 2.00; // Stores 200000

Of course, string output formatting of integer types would check the "DecimalPlaces" attribute in order to put the decimal point in the right place.

Andrew Phillips
aphillips @
GeneralImprovement Suggestion Pin
Jörgen Sigvardsson5-Dec-03 8:26
memberJörgen Sigvardsson5-Dec-03 8:26 
QuestionC# ? Pin
Richard Deeming17-Feb-03 23:02
memberRichard Deeming17-Feb-03 23:02 
AnswerRe: C# ? Pin
Marc Clifton18-Feb-03 1:48
memberMarc Clifton18-Feb-03 1:48 
GeneralPrecision Pin
Taka Muraoka17-Feb-03 12:18
memberTaka Muraoka17-Feb-03 12:18 
GeneralRe: Precision Pin
Marc Clifton17-Feb-03 13:24
memberMarc Clifton17-Feb-03 13:24 
GeneralInteresting Pin
Jörgen Sigvardsson17-Feb-03 11:59
memberJörgen Sigvardsson17-Feb-03 11:59 
GeneralRe: Interesting Pin
Peter Hancock17-Feb-03 12:28
memberPeter Hancock17-Feb-03 12:28 
GeneralRe: Interesting Pin
Jörgen Sigvardsson17-Feb-03 13:04
memberJörgen Sigvardsson17-Feb-03 13:04 
GeneralRe: Interesting Pin
Marc Clifton17-Feb-03 13:29
memberMarc Clifton17-Feb-03 13:29 
GeneralRe: Interesting Pin
Jörgen Sigvardsson17-Feb-03 13:41
memberJörgen Sigvardsson17-Feb-03 13:41 
GeneralRe: Interesting Pin
Peter Hancock17-Feb-03 14:30
memberPeter Hancock17-Feb-03 14:30 
GeneralRe: Interesting Pin
Jörgen Sigvardsson17-Feb-03 22:41
memberJörgen Sigvardsson17-Feb-03 22:41 
GeneralRe: Interesting Pin
Peter Hancock17-Feb-03 23:05
memberPeter Hancock17-Feb-03 23:05 
GeneralRe: Interesting Pin
Marc Clifton19-Feb-03 6:41
memberMarc Clifton19-Feb-03 6:41 
GeneralRe: Interesting Pin
Taka Muraoka17-Feb-03 18:04
memberTaka Muraoka17-Feb-03 18:04 
GeneralRe: Interesting Pin
Marc Clifton17-Feb-03 13:31
memberMarc Clifton17-Feb-03 13:31 
GeneralRe: Interesting Pin
Jörgen Sigvardsson17-Feb-03 13:46
memberJörgen Sigvardsson17-Feb-03 13:46 
GeneralRe: Interesting Pin
Marc Clifton17-Feb-03 13:55
memberMarc Clifton17-Feb-03 13:55 
GeneralRe: Interesting Pin
Jörgen Sigvardsson17-Feb-03 22:55
memberJörgen Sigvardsson17-Feb-03 22:55 
GeneralRe: Interesting Pin
Kris Vandermotten8-Oct-03 6:11
memberKris Vandermotten8-Oct-03 6:11 
GeneralRe: Interesting Pin
Jörgen Sigvardsson23-Feb-03 13:21
memberJörgen Sigvardsson23-Feb-03 13:21 
GeneralRe: Interesting Pin
brekehan12-Nov-14 13:01
memberbrekehan12-Nov-14 13:01 
GeneralRe: Interesting Pin
Marc Clifton12-Nov-14 13:15
protectorMarc Clifton12-Nov-14 13:15 
GeneralRe: Interesting Pin
Jörgen Sigvardsson9-Dec-14 1:00
memberJörgen Sigvardsson9-Dec-14 1:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.151002.1 | Last Updated 17 Feb 2003
Article Copyright 2003 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid