This is a simple utility class which encapsulates the OLE Automation type
DECIMAL. It's intended purpose is to rid the user of
::VarDecXXX() calls and make the
DECIMALs "behave just like
Decimal, inherits publically from
DECIMAL. The benefit of this is that anywhere you can put a
DECIMAL, you can also put a
Decimal. This makes the class fully interoperable with raw COM interfaces.
Unlike ATL, I do not overload the address of operator (
operator&), so there is no need to adapt this class with
CAdapt<> in order to contain it in STL containers.
All methods and constructors share the property
throw() - i.e., no method will ever throw an exception. However, in debug builds,
asserts may be triggered. Division by zero comes to mind. But you already know you shouldn't be dividing with zero, right?
All methods which return a
Decimal& value, returns a reference to
*this. Hence it is possible to stack such methods/operations.
There are ten constructors. They are:
Initializes the object with value 0.
Decimal(const DECIMAL& decValue)
Initializes the object with the value which is contained in parameter
Decimal(unsigned char nValue)
Decimal(unsigned short nValue)
Decimal(unsigned long nValue)
Initializes the object with the integer value contained in parameter
Initializes the object with the real value contained in parameter
The following operators are defined, and behaves just like
ints (hence I will not explain their usage and function!):
Decimal& operator = (char nValue)
Decimal& operator = (short nValue)
Decimal& operator = (long nValue)
Decimal& operator = (unsigned char nValue)
Decimal& operator = (unsigned short nValue)
Decimal& operator = (unsigned long nValue)
Decimal& operator = (float fValue)
Decimal& operator = (double dValue)
Decimal& operator += (const DECIMAL& decRHS)
Decimal& operator -= (const DECIMAL& decRHS)
Decimal& operator *= (const DECIMAL& decRHS)
Decimal& operator /= (const DECIMAL& decRHS)
Each operator operates just as if they had been defined for
bool FromString(LPCOLESTR lpszNum, LCID lcid = 0)
Parses a string pointed to by
lpszNum using the locale identifier
lcid. The parsed value is then assigned to
this object before returning.
Should this function fail because of a badly formatted string, the return value is
If the locale identifier,
0, then the default system locale will be used to parse the string.
Makes this value an absolute value.
Makes this value a negative value.
Makes this value an integer by removing its fractional part. The semantics of this function is as follows: If the value is negative, then the first negative integer less than or equal to this value is returned. (Semantic description was copied and adapted from the MSDN documentation. Please see manual page for
VarDecInt for the original text.)
Makes this value an integer by removing its fractional part. The semantics of this function is as follows: If the value is negative, then the first negative integer greater than or equal to this value is returned. (Semantic description was copied and adapted from the MSDN documentation. Please see manual page for
VarDecFix for the original text.)
Decimal& MakeRound(int n)
Rounds this value up to the
n must be greater or equal to zero.
The following functions are non-mutating. They are related to their mutating cousins described above, therefore I will just mention them here. For example, if a method mentioned below is named
Absolute, then please see the documentation above for
MakeAbsolute. These methods all share the common property that they do not modify the current value. Instead they create a temporary value which is modified and returned.
Decimal Absolute() const
Decimal Negative() const
Decimal Integer() const
Decimal Fixed() const
Decimal Round() const
bool IsNegative() const
Predicate which returns
true if this value is negative.
bool IsZero() const
Predicate which returns
true if this value is negative. This is the fastest way to test whether a
Decimal is zero or not.
BSTR ToString(LCID lcid = 0) const
Converts this value into a
BSTR string using the specified locale. If the locale identifier,
lcid, is zero, then the systems default locale will be used.
The following free functions, although logically members of
Decimal, works as if they had been designed for
Decimal operator + (const DECIMAL& decRHS, const DECIMAL& decLHS)
Decimal operator - (const DECIMAL& decRHS, const DECIMAL& decLHS)
Decimal operator * (const DECIMAL& decRHS, const DECIMAL& decLHS)
Decimal operator / (const DECIMAL& decRHS, const DECIMAL& decLHS)
bool operator < (const DECIMAL& decRHS, const DECIMAL& decLHS)
bool operator > (const DECIMAL& decRHS, const DECIMAL& decLHS)
bool operator <= (const DECIMAL& decRHS, const DECIMAL& decLHS)
bool operator >= (const DECIMAL& decRHS, const DECIMAL& decLHS)
bool operator == (const DECIMAL& decRHS, const DECIMAL& decLHS)
bool operator != (const DECIMAL& decRHS, const DECIMAL& decLHS)
I do not do any arithmetic calculations in my code. I merely use the OLE automation functions for manipulating
DECIMALs. All I have done is moulded it all together into a thin C++ wrapper class which makes
DECIMALs a bit more enjoyable to work with. Syntactic sugar makes software sweet.
If you take a quick look at the source file you will see that it is a very thin wrapper and adds, to my knowledge, a very small running time overhead. But please, correct me if I am wrong.
There are a couple of things which you may be interested in knowing before you use this code.
This code currently uses the C library versions of
assert (and my own
assert dependent macro
verify). You may want to change this before you use this in an ATL or MFC environment. A simple Search and Replace All should do the trick.
I have not included any method for dealing with
VARIANTs. I want a simple and clean interface. If I ever need any interoperability with
VARIANTs, I'll add the method. Until then, tough luck - or write it yourself.
As you may have noticed, I only support
BSTR strings in the interface. The reason is quite simple; I intend to use this code in the shadow lands near VB[Script] (and possibly other weird environments) where
BSTR is the string of choice. I don't believe in bloating interfaces, so I'll refrain from adding support for any other type of string unless I really need it. If I do, I'll update this source code. If you need it before I do, well, you've got the source!
I've included VC6 as a keyword for this article. But I admit that I have not tested against that compiler. But since I'm not using any fancy templates or other new and exotic features, I believe this code will work just fine in VC6. In fact, I had an older version of this class in another project, which compiled just fine for VC6. Considering that I've removed a lot of bloat, this class should still compile. This is QA at its peak.
There's a free function called
RoundToSmallestCoin in the header file. It's a function I use in POS-software I write at work. I keep it in this file because I'm lazy. If you feel that it's bloating your code, just select it and hit the delete button.
Marc Clifton wrote a similar article about a decimal class. His implementation is perfect if you do not intend to work with COM/OLE automation environments. You can find his article here.
Disclaimer and License grant
No warranties of any kind comes with this article and source code. I cannot be held liable for damages to data, damages to hardware or injuries. In fact, you cannot hold me liable for anything if you use this source code.
However, should you find this code useful, and should we ever meet, I will not say "No thank you" should you offer me a beer (preferably a beer of my choice). However, it's not a requirement. This is beerware if it's ok with you, otherwise it's freeware.