Introduction
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 DECIMAL
s "behave just like int
s".
Interoperability
My class, 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.
Interface
All methods and constructors share the property throw()
- i.e., no method will ever throw an exception. However, in debug builds, assert
s 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.
Constructors
There are ten constructors. They are:
Decimal()
Initializes the object with value 0.
Decimal(const DECIMAL& decValue)
Initializes the object with the value which is contained in parameter decValue
.
Decimal(char nValue)
Decimal(short nValue)
Decimal(long nValue)
Decimal(unsigned char nValue)
Decimal(unsigned short nValue)
Decimal(unsigned long nValue)
Initializes the object with the integer value contained in parameter nValue
.
Decimal(float fValue)
Decimal(double fValue)
Initializes the object with the real value contained in parameter nValue
.
Mutating operators
The following operators are defined, and behaves just like int
s (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)
Decimal operator++(int)
Decimal operator--(int)
Decimal& operator++()
Decimal& operator--()
Each operator operates just as if they had been defined for int
s.
Mutating methods
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 false
.
If the locale identifier, lcid
, is 0
, then the default system locale will be used to parse the string.
Decimal& MakeAbsolute()
Makes this value an absolute value.
Decimal& MakeNegative()
Makes this value a negative value.
Decimal& MakeInteger()
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.)
Decimal& MakeFixed()
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
th decimal.
Note that n
must be greater or equal to zero.
Non-Mutating methods
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
Inspectors
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.
Free functions
The following free functions, although logically members of Decimal
, works as if they had been designed for int
s.
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)
Implementation details
I do not do any arithmetic calculations in my code. I merely use the OLE automation functions for manipulating DECIMAL
s. All I have done is moulded it all together into a thin C++ wrapper class which makes DECIMAL
s 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.
Caveat emptor
There are a couple of things which you may be interested in knowing before you use this code.
verify/assert
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.
Variants
I have not included any method for dealing with VARIANT
s. I want a simple and clean interface. If I ever need any interoperability with VARIANT
s, I'll add the method. Until then, tough luck - or write it yourself.
Strings
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!
VC6
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.
Rounding coins
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.
References
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
Disclaimer
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.
License grant
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.